[dahdi-commits] dbailey: branch linux/dbailey/nlmstest r5429 - /linux/team/dbailey/nlmstest/d...
SVN commits to the DAHDI project
dahdi-commits at lists.digium.com
Tue Dec 2 13:56:24 CST 2008
Author: dbailey
Date: Tue Dec 2 13:56:23 2008
New Revision: 5429
URL: http://svn.digium.com/view/dahdi?view=rev&rev=5429
Log:
Add nlms echo canceller module for testing
Added:
linux/team/dbailey/nlmstest/drivers/dahdi/dahdi_echocan_nlms.c (with props)
linux/team/dbailey/nlmstest/drivers/dahdi/nlms.h (with props)
Modified:
linux/team/dbailey/nlmstest/drivers/dahdi/Kbuild
linux/team/dbailey/nlmstest/drivers/dahdi/Kconfig
Modified: linux/team/dbailey/nlmstest/drivers/dahdi/Kbuild
URL: http://svn.digium.com/view/dahdi/linux/team/dbailey/nlmstest/drivers/dahdi/Kbuild?view=diff&rev=5429&r1=5428&r2=5429
==============================================================================
--- linux/team/dbailey/nlmstest/drivers/dahdi/Kbuild (original)
+++ linux/team/dbailey/nlmstest/drivers/dahdi/Kbuild Tue Dec 2 13:56:23 2008
@@ -26,6 +26,7 @@
obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ECHOCAN_STEVE2) += dahdi_echocan_sec2.o
obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ECHOCAN_KB1) += dahdi_echocan_kb1.o
obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ECHOCAN_MG2) += dahdi_echocan_mg2.o
+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ECHOCAN_NLMS) += dahdi_echocan_nlms.o
obj-m += $(DAHDI_MODULES_EXTRA)
Modified: linux/team/dbailey/nlmstest/drivers/dahdi/Kconfig
URL: http://svn.digium.com/view/dahdi/linux/team/dbailey/nlmstest/drivers/dahdi/Kconfig?view=diff&rev=5429&r1=5428&r2=5429
==============================================================================
--- linux/team/dbailey/nlmstest/drivers/dahdi/Kconfig (original)
+++ linux/team/dbailey/nlmstest/drivers/dahdi/Kconfig Tue Dec 2 13:56:23 2008
@@ -66,6 +66,16 @@
---help---
To compile this driver as a module, choose M here: the
module will be called dahdi_echocancel_hpec.
+
+ If unsure, say Y.
+
+config DAHDI_ECHOCAN_NLMS
+ tristate "DADHI NLMS Echo Canceler"
+ depends on DAHDI_ECHOCAN
+ default DAHDI_ECHOCAN
+ ---help---
+ To compile this driver as a module, choose M here: the
+ module will be called dahdi_echocancel_nlms.
If unsure, say Y.
Added: linux/team/dbailey/nlmstest/drivers/dahdi/dahdi_echocan_nlms.c
URL: http://svn.digium.com/view/dahdi/linux/team/dbailey/nlmstest/drivers/dahdi/dahdi_echocan_nlms.c?view=auto&rev=5429
==============================================================================
--- linux/team/dbailey/nlmstest/drivers/dahdi/dahdi_echocan_nlms.c (added)
+++ linux/team/dbailey/nlmstest/drivers/dahdi/dahdi_echocan_nlms.c Tue Dec 2 13:56:23 2008
@@ -1,0 +1,492 @@
+/* dahdi_echocan_nlms.c
+ * based on aec.cpp
+ *
+ * Copyright (C) DFS Deutsche Flugsicherung (2004). All Rights
+ * Reserved. You are allowed to use this source code in any open
+ * source or closed source software you want. You are allowed to use
+ * the algorithms for a hardware solution. You are allowed to modify
+ * the source code. You are not allowed to remove the name of the
+ * author from this memo or from the source code files. You are not
+ * allowed to monopolize the source code or the algorithms behind the
+ * source code as your intellectual property. This source code is
+ * free of royalty and comes with no warranty.
+ *
+ * Modified by Doug Bailey (Digium Inc.) December 2008
+ * Modifications consist of:
+ * - Replacing floating variables with fixed point and moving a number of math
+ * operations to macros to faciliate fixed point math.
+ * - Adding API interface to allow for the code to reside within the DAHDI
+ * device driver
+ * - Pragmas have been added to allow the module to be compiled to
+ * run in user space for testing purposes.
+ *
+ */
+
+#include <linux/string.h>
+#include <linux/errno.h>
+#ifndef INUSER
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <asm/div64.h>
+#include <dahdi/kernel.h>
+#else
+#include <stdio.h>
+#include <stdlib.h>
+#endif
+
+#include "nlms.h"
+
+struct echo_can_state {
+ /* an arbitrary ID for this echo can - this really should be settable from the calling channel... */
+ int id;
+
+ /* pointer to allocated aec data */
+ struct AEC_FXP * aecdata;
+
+};
+
+/* init function */
+DRVR_STATIC struct AEC_FXP * init_AEC_FXP(int tail, int backup);
+
+/* destroy function */
+DRVR_STATIC void destroy_AEC_FXP(struct AEC_FXP* aec);
+
+/* Geigel Double-Talk Detector
+ *
+ * in d: microphone sample (PCM as REALing point value)
+ * in x: loudspeaker sample (PCM as REALing point value)
+ * return: 0 for no talking, 1 for talking
+ */
+DRVR_STATIC int FxPdtd(struct AEC_FXP* aec, FxPREAL d, FxPREAL x);
+
+/* Normalized Least Mean Square Algorithm pre-whitening (NLMS-pw)
+ * The LMS algorithm was developed by Bernard Widrow
+ * book: Widrow/Stearns, Adaptive Signal Processing, Prentice-Hall, 1985
+ *
+ * in mic: microphone sample (PCM as REALing point value)
+ * in spk: loudspeaker sample (PCM as REALing point value)
+ * in update: 0 for convolve only, 1 for convolve and update
+ * return: echo cancelled microphone sample
+ */
+DRVR_STATIC FxPREAL FxPnlms_pw(struct AEC_FXP* aec, FxPREAL mic, FxPREAL spk, int update);
+
+/* Acoustic Echo Cancellation and Suppression of one sample
+ * in d: microphone signal with echo
+ * in x: loudspeaker signal
+ * return: echo cancelled microphone signal
+ */
+DRVR_STATIC int doAEC_FXP(struct AEC_FXP* aec, int d, int x);
+
+
+DRVR_STATIC void FxPsetambient(struct AEC_FXP* aec, FxPREAL minxf);
+
+
+#define module_printk(level, fmt, args...) printk(level "%s: " fmt, THIS_MODULE->name, ## args)
+#define debug_printk(level, fmt, args...) if (debug >= level) printk("%s (%s): " fmt, THIS_MODULE->name, __FUNCTION__, ## args)
+
+
+/* Vector Dot Product */
+DRVR_STATIC FxPREAL dotp(FxPREAL a[], FxPREAL b[], int len) {
+ FxPREAL sum0 = 0, sum1 = 0;
+
+ int j;
+
+ for (j = 0; j < len; j+= 2) {
+ /* optimize: partial loop unrolling */
+ sum0 += (FxPMULT(a[j], b[j], Q_FRACT));
+ sum1 += (FxPMULT(a[j+1], b[j+1], Q_FRACT));
+ }
+ return (sum0+sum1);
+}
+
+DRVR_STATIC void FxPsetambient(struct AEC_FXP* aec, FxPREAL minxf) {
+ aec->dotp_xf_xf = aec->Min_dotp_xf_xf = FxPMULT(aec->tail, FxPMULT(minxf, minxf, Q_FRACT), Q_FRACT);
+}
+
+/* Exponential Smoothing or IIR Infinite Impulse Response Filter */
+DRVR_STATIC FxPREAL iir_highpass(FxPREAL *x, FxPREAL in)
+{
+ FxPREAL result;
+ const FxPREAL a0 = COEF(0.01f, Q_FRACT);
+ *x+= FxPMULT(a0, (in-*x), Q_FRACT);
+ result = in-*x;
+ return result;
+}
+
+
+/* 13 taps FIR Finite Impulse Response filter
+ * Coefficients calculated with
+ * www.dsptutor.freeuk.com/KaiserFilterDesign/KaiserFilterDesign.html
+ */
+DRVR_STATIC FxPREAL fir_highpass(FxPREAL *z, FxPREAL in)
+{
+ FxPREAL sum0 = 0, sum1 = 0;
+ int j;
+
+ const FxPREAL a[14] = {
+ /* Kaiser Window FIR Filter, Filter type: High pass */
+ /* Passband: 300.0 - 4000.0 Hz, Order: 12 */
+ /* Transition band: 100.0 Hz, Stopband attenuation: 10.0 dB */
+ COEF(-0.043183226f,Q_FRACT), COEF(-0.046636667f,Q_FRACT), COEF(-0.049576525f,Q_FRACT), COEF(-0.051936015f,Q_FRACT),
+ COEF(-0.053661242f,Q_FRACT), COEF(-0.054712527f,Q_FRACT), COEF(0.82598513f,Q_FRACT), COEF(-0.054712527f,Q_FRACT),
+ COEF(-0.053661242f,Q_FRACT), COEF(-0.051936015f,Q_FRACT), COEF(-0.049576525f,Q_FRACT), COEF(-0.046636667f,Q_FRACT),
+ COEF(-0.043183226f,Q_FRACT), 0
+ };
+
+ memmove(z+1, z, 13*sizeof(FxPREAL));
+ z[0] = in;
+
+ for (j = 0; j < 14; j+= 2) {
+ /* optimize: partial loop unrolling */
+ sum0 += FxPMULT(a[j], z[j], Q_FRACT);
+ sum1 += FxPMULT(a[j+1], z[j+1], Q_FRACT);
+ }
+ return sum0+sum1;
+}
+
+/* Recursive single pole IIR Infinite Impulse response filter
+ * Coefficients calculated with
+ * http:// www.dsptutor.freeuk.com/IIRFilterDesign/IIRFiltDes102.html
+ */
+DRVR_STATIC FxPREAL iir1_highpass(FxPREAL *x, FxPREAL *y, FxPREAL in)
+{
+ /* Chebyshev IIR filter, Filter type: HP */
+ /* Passband: 3700 - 4000.0 Hz */
+ /* Passband ripple: 1.5 dB, Order: 1 */
+ const FxPREAL a0 = COEF(0.105831884f, Q_FRACT);
+ const FxPREAL a1 = COEF(-0.105831884, Q_FRACT);
+ const FxPREAL b1 = COEF(0.78833646f, Q_FRACT);
+ FxPREAL out = FxPMULT(a0, in, Q_FRACT) +
+ FxPMULT(a1, *x, Q_FRACT) +
+ FxPMULT(b1, *y, Q_FRACT);
+ *x = in;
+ *y = out;
+ return out;
+}
+
+DRVR_STATIC struct AEC_FXP* init_AEC_FXP(int tail, int backup)
+{
+ struct AEC_FXP *aec;
+ if (!(aec = (struct AEC_FXP*)kmalloc(sizeof(struct AEC_FXP), GFP_KERNEL)))
+ goto nomemory_cleanup;
+ memset(aec, 0, sizeof(struct AEC_FXP));
+
+ if (!(aec->max_x = kmalloc(tail/DTD_LEN * sizeof(FxPREAL), GFP_KERNEL)))
+ goto nomemory_cleanup;
+ memset(aec->max_x, 0, tail/DTD_LEN * sizeof(FxPREAL));
+ if (!(aec->x = kmalloc((tail + NLMS_EXT) * sizeof(FxPREAL), GFP_KERNEL)))
+ goto nomemory_cleanup;
+ memset(aec->x, 0, (tail + NLMS_EXT) * sizeof(FxPREAL));
+ if (!(aec->xf = kmalloc((tail + NLMS_EXT) * sizeof(FxPREAL), GFP_KERNEL)))
+ goto nomemory_cleanup;
+ memset(aec->xf, 0, (tail + NLMS_EXT) * sizeof(FxPREAL));
+ if (!(aec->w = kmalloc(tail * sizeof(FxPREAL), GFP_KERNEL)))
+ goto nomemory_cleanup;
+ memset(aec->w, 0, tail * sizeof(FxPREAL));
+
+ if (!(aec->backup_w = kmalloc(tail * sizeof(FxPREAL), GFP_KERNEL)))
+ goto nomemory_cleanup;
+ memset(aec->backup_w, 0 , tail * sizeof(FxPREAL));
+
+ aec->tail = tail;
+ aec->j = NLMS_EXT;
+ aec->s0avg = COEF(M80dB_PCM, Q_FRACT);
+ FxPsetambient(aec, Min_xf);
+
+ return aec;
+
+nomemory_cleanup:
+ destroy_AEC_FXP(aec);
+ return NULL;
+}
+
+DRVR_STATIC void destroy_AEC_FXP(struct AEC_FXP* aec)
+{
+ if (aec) {
+ if (aec->w) {
+ kfree(aec->w);
+ aec->w = NULL;
+ }
+ if (aec->xf) {
+ kfree(aec->xf);
+ aec->xf = NULL;
+ }
+ if (aec->x) {
+ kfree(aec->x);
+ aec->x = NULL;
+ }
+ if (aec->max_x) {
+ kfree(aec->max_x);
+ aec->max_x = NULL;
+ }
+ if (aec->backup_w) {
+ kfree(aec->backup_w);
+ aec->max_x = NULL;
+ }
+ kfree(aec);
+ }
+}
+
+
+DRVR_STATIC void FxPrestore_backup(struct AEC_FXP *aec)
+{
+ memcpy(aec->w, aec->backup_w, sizeof(FxPREAL)*aec->tail);
+ aec->lastbackup = 0;
+}
+
+DRVR_STATIC FxPREAL FxPnlms_pw(struct AEC_FXP *aec, FxPREAL mic, FxPREAL spk, int update)
+{
+ FxPREAL d, e, ef, delta;
+ FxPREAL db1, db2;
+
+ d = mic; /* desired signal */
+ aec->x[aec->j] = spk;
+ aec->xf[aec->j] = iir1_highpass(&aec->Fxx, &aec->Fxy, spk); /* pre-whitening of x */
+
+ /* calculate error value */
+ /* (mic signal - estimated mic signal from spk signal) */
+ delta = dotp(aec->w, aec->x + aec->j, aec->tail);
+
+ if (FxPABS(delta) > COEF(MAX_ADJUSTMENT, Q_FRACT))
+ {
+#ifdef INUSER
+ printf("Fix Point: Too big of step. Input mic is %x spk is %x\n", mic, spk); /* Enable this debug for user space */
+ printf("Fix Point: delta=0x%x sig=0x%x Q_FRACT=%d\n", delta, d, Q_FRACT);
+#endif
+ FxPrestore_backup(aec);
+ delta = dotp(aec->w, aec->x + aec->j, aec->tail);
+ update=0;
+ }
+
+ if (update && aec->lastbackup > BACKUP_INTERVAL) {
+ memcpy(aec->backup_w, aec->w, sizeof(FxPREAL)*aec->tail);
+ aec->lastbackup = 0;
+ }
+
+ e = d - delta;
+ ef = iir1_highpass(&aec->Fex, &aec->Fey, e); /* pre-whitening of e */
+ /* optimize: iterative dotp(xf, xf) */
+
+#if 0
+ aec->dotp_xf_xf += (FxPMULT(aec->xf[aec->j], aec->xf[aec->j], Q_FRACT) -
+ FxPMULT(aec->xf[aec->j+aec->tail-1], aec->xf[aec->j+aec->tail-1], Q_FRACT));
+#else
+ /* squares of values can overflow so more range/less precision is used here. */
+ db1 = aec->xf[aec->j] >>3;
+ db1 = FxPMULT(db1, db1, Q_FRACT-3);
+ db2 = aec->xf[aec->j+aec->tail-1]>>3;
+ db2 = FxPMULT(db2, db2, Q_FRACT-3);
+ aec->dotp_xf_xf += ((db1 - db2) << 3);
+
+#endif
+ if (update) {
+ /* calculate variable step size */
+ FxPREAL interim = FxPMULT(COEF(Stepsize, Q_FRACT), ef, Q_FRACT);
+ FxPREAL mikro_ef = FxPDIV( interim, aec->dotp_xf_xf, Q_FRACT);
+
+ /* update tap weights (filter learning) */
+ int i;
+ for (i = 0; i < aec->tail; i += 2) {
+ /* optimize: partial loop unrolling */
+ aec->w[i] += FxPMULT(mikro_ef, aec->xf[i+aec->j], Q_FRACT);
+ aec->w[i+1] += FxPMULT(mikro_ef, aec->xf[i+aec->j+1], Q_FRACT);
+ }
+ }
+
+ if (--(aec->j) < 0) {
+ /* optimize: decrease number of memory copies */
+ aec->j = NLMS_EXT;
+ memmove(aec->x+aec->j+1, aec->x, (aec->tail-1)*sizeof(FxPREAL));
+ memmove(aec->xf+aec->j+1, aec->xf, (aec->tail-1)*sizeof(FxPREAL));
+ }
+
+ return e;
+}
+
+
+DRVR_STATIC int FxPdtd(struct AEC_FXP *aec, FxPREAL d, FxPREAL x)
+{
+ int i;
+
+ /* optimized implementation of max(|x[0]|, |x[1]|, .., |x[L-1]|): */
+ /* calculate max of block (DTD_LEN values) */
+ if (x<0) /* fabsf */
+ x = -x;
+
+ if (x > aec->max_x[aec->dtdNdx]) {
+ aec->max_x[aec->dtdNdx] = x;
+ if (x > aec->max_max_x) {
+ aec->max_max_x = x;
+ }
+ }
+ if (++(aec->dtdCnt) >= DTD_LEN) {
+ int max_i = aec->tail/DTD_LEN;
+
+ aec->dtdCnt = 0;
+ /* calculate max of max */
+ aec->max_max_x = 0;
+ for (i = 0; i < max_i; ++i) {
+ if (aec->max_x[i] > aec->max_max_x) {
+ aec->max_max_x = aec->max_x[i];
+ }
+ }
+ /* rotate Ndx */
+ if (++(aec->dtdNdx) >= max_i)
+ aec->dtdNdx = 0;
+ aec->max_x[aec->dtdNdx] = 0;
+ }
+
+ /* The Geigel DTD algorithm with Hangover timer Thold */
+ if (d<0)
+ d = -d;
+
+ if (d >= FxPMULT(COEF(GeigelThreshold, Q_FRACT), aec->max_max_x, Q_FRACT)) {
+ aec->hangover = Thold;
+ }
+
+ if (aec->hangover)
+ aec->hangover--;
+
+ return (aec->hangover > 0);
+}
+
+
+DRVR_STATIC int doAEC_FXP(struct AEC_FXP *aec, int d, int x)
+{
+ int update;
+ FxPREAL s0 = FxPCONV(d, Q_FRACT);
+ FxPREAL s1 = FxPCONV(x, Q_FRACT);
+
+ FxPREAL s0abs;
+ FxPREAL s1abs;
+
+ /* Mic Highpass Filter - to remove DC */
+ s0 = iir_highpass(&aec->hp00, s0);
+
+ /* Mic Highpass Filter - telephone users are used to 300Hz cut-off */
+ s0 = fir_highpass(aec->hp0, s0);
+
+ s0abs = (s0 > 0) ? s0 : -s0;
+ s1abs = (s1 > 0) ? s1 : -s1;
+
+ /* ambient mic level estimation */
+/* aec->s0avg += FxPMULT( COEF( 1e-4f, Q_FRACT), (s0abs - aec->s0avg), Q_FRACT); ** This is beyond precision of FxPREAL */
+ aec->s0avg += (s0abs - aec->s0avg) >> Q_FRACT; /* Make this value small to soften response */
+ /* Spk Highpass Filter - to remove DC */
+ s1 = iir_highpass(&aec->hp1, s1);
+
+ /* silence detection */
+ if (s0abs < COEF(SilenceThreshold, Q_FRACT) || s1abs < COEF(SilenceThreshold, Q_FRACT )) {
+ aec->silence_count++;
+ }
+ else {
+ aec->silence_count = 0;
+ }
+
+ /* Don't do anything on silence */
+ if (aec->silence_count < aec->tail) {
+
+ /* Double Talk Detector */
+ update = !FxPdtd(aec, s0, s1);
+
+ /* Acoustic Echo Cancellation */
+ s0 = FxPnlms_pw(aec, s0, s1, update);
+
+ /* Acoustic Echo Suppression */
+ if (update) {
+ /* Non Linear Processor (NLP): attenuate low volumes */
+ s0 = FxPMULT(s0, COEF(NLPAttenuation, Q_FRACT), Q_FRACT);
+ }
+ }
+
+ /* Saturation */
+ if (s0 > COEF(MAXPCM, Q_FRACT)) {
+ return (int)MAXPCM;
+ } else if (s0 < COEF(-MAXPCM, Q_FRACT)) {
+ return (int)-MAXPCM;
+ } else {
+ return (int)(FxPREVERT(s0, Q_FRACT));
+ }
+}
+
+DRVR_STATIC void echo_can_free(struct echo_can_state *ec)
+{
+ destroy_AEC_FXP(ec->aecdata);
+ kfree(ec);
+
+}
+
+#ifndef INUSER
+/********************************************************************************
+* Interface functions for nlms echo canceller within DAHDI driver
+*/
+static void echo_can_update(struct echo_can_state *ec, short *iref, short *isig)
+{
+ unsigned int x;
+ short result;
+
+ for (x = 0; x < DAHDI_CHUNKSIZE; x++) {
+ result = doAEC_FXP(ec->aecdata, *isig, *iref);
+ *isig = result;
+ ++isig;
+ ++iref;
+ }
+}
+
+static int echo_can_create(struct dahdi_echocanparams *ecp, struct dahdi_echocanparam *p,
+ struct echo_can_state **ec)
+{
+ int size = sizeof(**ec);
+
+ if (!(*ec = kmalloc(size, GFP_KERNEL)))
+ return -ENOMEM;
+
+ memset(*ec, 0, size);
+
+ (*ec)->aecdata = init_AEC_FXP(ecp->tap_length, 0);
+
+ return 0;
+}
+
+static int echo_can_traintap(struct echo_can_state *ec, int pos, short val)
+{
+ /* Nothing done here */
+ return 0;
+}
+
+static const struct dahdi_echocan me = {
+ .name = "NLMS",
+ .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 int __init mod_init(void)
+{
+
+
+ if (dahdi_register_echocan(&me)) {
+ module_printk(KERN_ERR, "could not register with DAHDI core\n");
+
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+static void __exit mod_exit(void)
+{
+ dahdi_unregister_echocan(&me);
+}
+
+
+MODULE_DESCRIPTION("DAHDI 'NLMS' Echo Canceler");
+MODULE_AUTHOR("Doug Bailey");
+MODULE_LICENSE("GPL v2");
+
+module_init(mod_init);
+module_exit(mod_exit);
+#endif
Propchange: linux/team/dbailey/nlmstest/drivers/dahdi/dahdi_echocan_nlms.c
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: linux/team/dbailey/nlmstest/drivers/dahdi/dahdi_echocan_nlms.c
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: linux/team/dbailey/nlmstest/drivers/dahdi/dahdi_echocan_nlms.c
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: linux/team/dbailey/nlmstest/drivers/dahdi/nlms.h
URL: http://svn.digium.com/view/dahdi/linux/team/dbailey/nlmstest/drivers/dahdi/nlms.h?view=auto&rev=5429
==============================================================================
--- linux/team/dbailey/nlmstest/drivers/dahdi/nlms.h (added)
+++ linux/team/dbailey/nlmstest/drivers/dahdi/nlms.h Tue Dec 2 13:56:23 2008
@@ -1,0 +1,298 @@
+/* nlms_fxp.h
+ * based on aec.h
+ *
+ * Copyright (C) DFS Deutsche Flugsicherung (2004). All Rights
+ * Reserved. You are allowed to use this source code in any open
+ * source or closed source software you want. You are allowed to use
+ * the algorithms for a hardware solution. You are allowed to modify
+ * the source code. You are not allowed to remove the name of the
+ * author from this memo or from the source code files. You are not
+ * allowed to monopolize the source code or the algorithms behind the
+ * source code as your intellectual property. This source code is
+ * free of royalty and comes with no warranty.
+ *
+ * Modified by Doug Bailey (Digium Inc.) December 2008
+ * Modifications consist of:
+ * - Replacing floating variables with fixed point and moving a number of math
+ * operations to macros to faciliate fixed point math.
+ * - Adding API interface to allow for the code to reside within the DAHDI
+ * device driver
+ * - Pragmas have been added to allow the module to be compiled to
+ * run in user space for testing purposes.
+ */
+
+#ifndef _NLMS_H /* include only once */
+
+#include <linux/types.h>
+
+
+#ifdef INUSER /* If compiled to run in user space */
+#include <limits.h>
+
+typedef long long s64;
+typedef unsigned int uint32_t;
+#define DRVR_STATIC
+#define kmalloc(A,B) malloc((A))
+#define kfree(A) free((A))
+#define printk printf
+#define GFP_KERNEL 0
+
+#else
+#define DRVR_STATIC static
+#endif
+
+
+/********************************************
+* Fixed Point Number (FxPREAL) is defined as:
+* Sign bit | Q_INT | Q_FRACT |
+* Where :
+* Q_INT is the number of bits reserved for the integer portion of the number
+* (Thus determining range)
+* Q_FRACT is the number of bits reserved for the fractional portion of the number
+* (Thus determining precision of the number)
+*/
+typedef int FxPREAL;
+
+#define Q_FRACT (6)
+#define Q_INT ((sizeof (FxPREAL) * CHAR_BIT) -1 - Q_FRACT)
+
+
+/* conversion to fixed point values
+ * FWL - Fractional Word length - Number of bits allocated to fractional portion of the number.
+ */
+
+#define COEF(VAL, FWL) (FxPREAL) ((float)(1 << (FWL)) * (VAL) )
+
+#ifdef CHECKFOR_OVER
+static inline FxPREAL __FxPMULT(FxPREAL FxPA, FxPREAL FxPB, int FWL, char * function, int line)
+{
+ long long newresult;
+ newresult = (long long)FxPA * (long long)FxPB;
+ if (newresult < 0)
+ newresult *= -1;
+ if (newresult & 0xFFFFFFFF80000000) {
+ printf ("%s:%d, Result overflow (%llx) when mult %d with %d\n", function, line, newresult, FxPA, FxPB );
+ }
+ else if (newresult & 0x40000000) {
+ printf ("%s:%d, Result coming close (%llx) when mult %d with %d\n", function, line, newresult, FxPA, FxPB );
+ }
+
+ return (((FxPA) * (FxPB)) >> (FWL));
+}
+#define FxPMULT(FxPA, FxPB, FWL) (__FxPMULT((FxPA), (FxPB), (FWL), __FUNCTION__, __LINE__))
+
+#else
+#define FxPMULT(FxPA, FxPB, FWL) (((FxPA) * (FxPB)) >> (FWL))
+#endif
+
+#ifdef INUSER
+static inline FxPREAL FxPDIV(FxPREAL dividend, FxPREAL divisor, int fwl)
+{
+#ifdef CHECKFOR_OVER
+ unsigned int mask = ((1 << fwl) -1);
+ mask <<= ((sizeof (FxPREAL) * CHAR_BIT) -1) -fwl;
+
+ if ((0 > dividend && 0 != ((dividend * -1) & mask)) ||
+ (0 < dividend && 0 != (dividend & mask))) {
+ printf ("Loss of data in dividend (%x with mask %x)\n", dividend, mask);
+
+ }
+#endif
+ return (dividend << Q_FRACT)/divisor;
+
+}
+
+#else
+#if 0 /* 64 bit divide */
+static inline FxPREAL FxPDIV(FxPREAL dividend, FxPREAL divisor, int fwl)
+{
+ uint32_t nudivisor;
+ int negate = 0;
+
+ if (0 > divisor) {
+ negate ^= 1;
+ nudivisor = (uint32_t)(-1 * divisor);
+ } else {
+ nudivisor = (uint32_t)divisor;
+ }
+
+ if (0 > dividend) {
+ negate ^= 1;
+ dividend *= -1;
+ }
+
+ dividend = dividend << fwl;
+ do_div(dividend, nudivisor);
+ if (negate) {
+ dividend = -1 * dividend;
+ }
+ return dividend;
+
+}
+#else
+static inline FxPREAL FxPDIV(FxPREAL dividend, FxPREAL divisor, int fwl)
+{
+ return (dividend << Q_FRACT)/divisor;
+
+}
+#endif
+#endif
+
+#define FxPCONV(FxPA, FWL) ((FxPREAL)((FxPA) * (1 << (FWL))))
+
+#define FxPREVERT(FxPA, FWL) ((FxPA) >> (FWL))
+
+#define FxPABS(FxPA) (0 > (FxPA) ? (-1 * (FxPA)):(FxPA))
+
+
+/* dB Values */
+#define M0dB 1.0f
+#define M3dB 0.71f
+#define M6dB 0.50f
+#define M9dB 0.35f
+#define M12dB 0.25f
+#define M18dB 0.125f
+#define M24dB 0.063f
+
+/* dB values for 16bit PCM */
+/* MxdB_PCM 32767 * 10 ^(x / 20) */
+#define M10dB_PCM 10362.0f
+#define M20dB_PCM 3277.0f
+#define M25dB_PCM 1843.0f
+#define M30dB_PCM 1026.0f
+#define M35dB_PCM 583.0f
+#define M40dB_PCM 328.0f
+#define M45dB_PCM 184.0f
+#define M50dB_PCM 104.0f
+#define M55dB_PCM 58.0f
+#define M60dB_PCM 33.0f
+#define M65dB_PCM 18.0f
+#define M70dB_PCM 10.0f
+#define M75dB_PCM 6.0f
+#define M80dB_PCM 3.0f
+#define M85dB_PCM 2.0f
+#define M90dB_PCM 1.0f
+
+#define MAXPCM 32767.0f
+
+/* Design constants (Change to fine tune the algorithms */
+
+/* The following values are for hardware AEC and studio quality
+ * microphone */
+
+/* convergence speed. Range: >0 to <1 (0.2 to 0.7). Larger values give
+ * more AEC in lower frequencies, but less AEC in higher frequencies. */
+#define Stepsize 0.7f
+
+/* minimum energy in xf. Range: M70dB_PCM to M50dB_PCM. Should be equal
+ * to microphone ambient Noise level */
+#define Min_xf M50dB_PCM
+
+/* Double Talk Detector Speaker/Microphone Threshold. Range <=1
+ * Large value (M0dB) is good for Single-Talk Echo cancellation,
+ * small value (M12dB) is good for Doulbe-Talk AEC */
+#define GeigelThreshold M6dB
+
+/* Double Talk Detector hangover in taps. Not relevant for Single-Talk
+ * AEC */
+#define Thold 30*8
+
+/* Threshold for silence detection */
+#define SilenceThreshold M85dB_PCM
+
+/* for Non Linear Processor. Range >0 to 1. Large value (M0dB) is good
+ * for Double-Talk, small value (M12dB) is good for Single-Talk */
+#define NLPAttenuation M12dB
+
+/* Below this line there are no more design constants */
+
+/* Extention in taps to reduce mem copies */
+#define NLMS_EXT (10*8)
+
+/* block size in taps to optimize DTD calculation */
+#define DTD_LEN 16
+
+#define BACKUP_INTERVAL 256 /* backup coefficents every X samples */
+#define MAX_ADJUSTMENT 7000 /* adjustments over this amount are considered an error */
+
+struct AEC_FXP
+{
+ /* maximum NLMS filter length in taps. A longer filter length gives
+ * better Echo Cancellation, but slower convergence speed and
+ * needs more CPU power (Order of NLMS is linear) */
+ int tail;
+
+ /* Time domain Filters */
+ FxPREAL hp00, hp1; /* DC-level remove Highpass) */
+ FxPREAL hp0[14]; /* 300Hz cut-off Highpass */
+
+ FxPREAL Fxx, Fxy; /* IIR1 */
+ FxPREAL Fex, Fey;
+
+ /* Geigel DTD (Double Talk Detector) */
+ FxPREAL max_max_x; /* max(|x[0]|, .. |x[L-1]|) */
+ int hangover;
+ /* optimize: less calculations for max() */
+ FxPREAL *max_x;
+ int dtdCnt;
+ int dtdNdx;
+
+ /* NLMS-pw */
+ FxPREAL *x; /* tap delayed loudspeaker signal */
+ FxPREAL *xf; /* pre-whitening tap delayed signal */
+ FxPREAL *w; /* tap weights */
+ int j; /* optimize: less memory copies */
+ FxPREAL dotp_xf_xf; /* double to avoid loss of precision */
+ int silence_count; /* how many successive frames of silence */
+ FxPREAL Min_dotp_xf_xf;
+ FxPREAL s0avg;
+
+ /* backup coefficents */
+ FxPREAL *backup_w; /* tap weights */
+ int lastbackup;
+
+};
+
+#ifdef INUSER
+/* init function */
+struct AEC_FXP * init_AEC_FXP(int tail, int backup);
+
+/* destroy function */
+void destroy_AEC_FXP(struct AEC_FXP* aec);
+
+/* Geigel Double-Talk Detector
+ *
+ * in d: microphone sample (PCM as REALing point value)
+ * in x: loudspeaker sample (PCM as REALing point value)
+ * return: 0 for no talking, 1 for talking
+ */
+int FxPdtd(struct AEC_FXP* aec, FxPREAL d, FxPREAL x);
+
+/* Normalized Least Mean Square Algorithm pre-whitening (NLMS-pw)
+ * The LMS algorithm was developed by Bernard Widrow
+ * book: Widrow/Stearns, Adaptive Signal Processing, Prentice-Hall, 1985
+ *
+ * in mic: microphone sample (PCM as REALing point value)
+ * in spk: loudspeaker sample (PCM as REALing point value)
+ * in update: 0 for convolve only, 1 for convolve and update
+ * return: echo cancelled microphone sample
+ */
+FxPREAL FxPnlms_pw(struct AEC_FXP* aec, FxPREAL mic, FxPREAL spk, int update);
+
+/* Acoustic Echo Cancellation and Suppression of one sample
+ * in d: microphone signal with echo
+ * in x: loudspeaker signal
+ * return: echo cancelled microphone signal
+ */
+int doAEC_FXP(struct AEC_FXP* aec, int d, int x);
+
+
+/*static float FxPgetambient(struct AEC_FXP* aec); **/
+void FxPsetambient(struct AEC_FXP* aec, FxPREAL minxf);
+
+#endif
+
+#define _NLMS_H
+#endif
+
+
Propchange: linux/team/dbailey/nlmstest/drivers/dahdi/nlms.h
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: linux/team/dbailey/nlmstest/drivers/dahdi/nlms.h
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: linux/team/dbailey/nlmstest/drivers/dahdi/nlms.h
------------------------------------------------------------------------------
svn:mime-type = text/plain
More information about the dahdi-commits
mailing list