[svn-commits] dbailey: branch linux/dbailey/nlmstest r5429 - /linux/team/dbailey/nlmstest/d...

SVN commits to the Digium repositories svn-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 svn-commits mailing list