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>