Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 26 Dec 1998 20:56:09 +0100
From:      Poul-Henning Kamp <phk@critter.freebsd.dk>
To:        freebsd-isdn@FreeBSD.ORG
Subject:   HOWTO: DTMF decoding in software.
Message-ID:  <65021.914702169@critter.freebsd.dk>
In-Reply-To: Your message of "Wed, 23 Dec 1998 12:29:13 %2B0100." <m0zsmTZ-0000f3C@hcswork.hcs.de> 

next in thread | previous in thread | raw e-mail | index | archive | help

I remember that somebody asked about this long time ago, so I sat
down and hacked a digital filter for that.

The following piece of code will read a ".g711a" file, and output
9 columns of data.  The first is the linear value of the sample,
the other 8 are strength of the 8 DTMF tones.  

Try to run the "beep.g711a" file from i4b through it, and plot the
output columns with gnuplot.  It seems Hellmutt pressed a '1' :-)

The implementation is a recursive resonance filter, actually 8 of
them, one for each frequency, done in floating point.  With a little
attention to rounding, it can be done just as good, and much faster
in integer math, in fact 16 bit should be enough, but may not be
faster than 32bit.

The "POLRAD" quantity determines the resonance width of the filters,
if you make it too low, it will confuse tones and recognize them
where they are not.  If you make it too high (never, ever >= 1.0!)
it will take longer to react and maybe not catch a slightly offbeat
tone.  If you set it above or equal to 1.0 you get a tone generator.

This could also be a good basis for a 300Baud FSK modem emulation.

It seems that the .g711a files are bit-flipped, therefore the flip[]
array trick in this code.  The alaw->linear converter is lifted from
sox.

Now, who writes the answering-machine to end all answering machines
for i4b ?

Poul-Henning

----------------------------------------------------------------------
#include <stdio.h>
#include <math.h>

/*
 * g711.c
 *
 * u-law, A-law and linear PCM conversions.
 */
#define SIGN_BIT        (0x80)          /* Sign bit for a A-law byte. */
#define QUANT_MASK      (0xf)           /* Quantization field mask. */
#define NSEGS           (8)             /* Number of A-law segments. */
#define SEG_SHIFT       (4)             /* Left shift for segment number. */
#define SEG_MASK        (0x70)          /* Segment field mask. */

static short seg_end[8] = {0xFF, 0x1FF, 0x3FF, 0x7FF,
                            0xFFF, 0x1FFF, 0x3FFF, 0x7FFF};

/*
 * alaw2linear() - Convert an A-law value to 16-bit linear PCM
 *
 */
int
alaw2linear(a_val)
        unsigned char   a_val;
{
        int             t;
        int             seg;

        a_val ^= 0x55;

        t = (a_val & QUANT_MASK) << 4;
        seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
        switch (seg) {
        case 0:
                t += 8;
                break;
        case 1:
                t += 0x108;
                break;
        default:
                t += 0x108;
                t <<= seg - 1;
        }
        return ((a_val & SIGN_BIT) ? t : -t);
}

int flip[256];

double dtmf[8] = {697, 770, 852, 941, 1209, 1336, 1477, 1633};
double p1[8];

/* This is the Q of the filter (pole radius).  must be less than 1.0 */
#define POLRAD .99

#define P2 (POLRAD*POLRAD)

main()
{
	int i, j, kk;
	double x, a[8], b[8], c[8], d[8], e[8], f[8], g[8], h[8], k[8], l[8], m[8], n[8], y[8];


	for (kk = 0; kk < 8; kk++) {
		g[kk] = k[kk] = 0.0;
		p1[kk] = (-cos(2 * 3.141592 * dtmf[kk] / 8000.0));
	}

	for (i=0;i<256;i++) {
		flip[i]  = (i &   1) << 7;
		flip[i] |= (i &   2) << 5;
		flip[i] |= (i &   4) << 3;
		flip[i] |= (i &   8) << 1;
		flip[i] |= (i &  16) >> 1;
		flip[i] |= (i &  32) << 3;
		flip[i] |= (i &  64) << 5;
		flip[i] |= (i & 128) << 7;
	}
	x = 0.0;
	while ((i = getchar()) != EOF) {
		i = flip[i];
		j = alaw2linear(i);

		x = j / 32768.0;
		printf(" %g", x);
		for(kk = 0; kk < 8; kk++) {
			a[kk] = x;
			h[kk] = g[kk];
			l[kk] = k[kk];

			b[kk] = a[kk] - l[kk];
			c[kk] = P2 * b[kk];
			d[kk] = a[kk] + c[kk];
			e[kk] = d[kk] - h[kk];
			f[kk] = p1[kk] * e[kk];
			g[kk] = f[kk] + d[kk];
			k[kk] = h[kk] + f[kk];
			m[kk] = l[kk] + c[kk];
			n[kk] = a[kk] - m[kk];
			printf(" %g", n[kk]);
		}
		printf("\n");
	}
	return (0);
}
----------------------------------------------------------------------

--
Poul-Henning Kamp             FreeBSD coreteam member
phk@FreeBSD.ORG               "Real hackers run -current on their laptop."
"ttyv0" -- What UNIX calls a $20K state-of-the-art, 3D, hi-res color terminal

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-isdn" in the body of the message



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?65021.914702169>