Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Re: Parsing touch tone input?

by Anonymous Monk
on Jul 20, 2002 at 21:48 UTC ( #183683=note: print w/ replies, xml ) Need Help??


in reply to Parsing touch tone input?

Dunno if this will help, It's in C but you might be able to convert it into perl.

/* * detect.c * This program will detect MF tones and normal * dtmf tones as well as some other common tones such * as BUSY, DIALTONE and RING. * The program uses a goertzel algorithm to detect * the power of various frequency ranges. * * input is assumed to be 8 bit samples. The program * can use either signed or unsigned samples according * to a compile time option: * * cc -DUNSIGNED detect.c -o detect * * for unsigned input (soundblaster) and: * * cc detect.c -o detect * * for signed input (amiga samples) * if you dont want flushes, -DNOFLUSH * * Tim N. */ #include <stdio.h> #include <math.h> #include "detect.h" /* * calculate the power of each tone according * to a modified goertzel algorithm described in * _digital signal processing applications using the * ADSP-2100 family_ by Analog Devices * * input is 'data', N sample values * * ouput is 'power', NUMTONES values * corresponding to the power of each tone */ calc_power(data,power) #ifdef UNSIGNED unsigned char *data; #else char *data; #endif float *power; { float u0[NUMTONES],u1[NUMTONES],t,in; int i,j; for(j=0; j<NUMTONES; j++) { u0[j] = 0.0; u1[j] = 0.0; } for(i=0; i<N; i++) { /* feedback */ #ifdef UNSIGNED in = ((int)data[i] - 128) / 128.0; #else in = data[i] / 128.0; #endif for(j=0; j<NUMTONES; j++) { t = u0[j]; u0[j] = in + coef[j] * u0[j] - u1[j]; u1[j] = t; } } for(j=0; j<NUMTONES; j++) /* feedforward */ power[j] = u0[j] * u0[j] + u1[j] * u1[j] - coef[j] * u0[j] * u1[j] +; return(0); } /* * detect which signals are present. * * return values defined in the include file * note: DTMF 3 and MF 7 conflict. To resolve * this the program only reports MF 7 between * a KP and an ST, otherwise DTMF 3 is returned */ decode(data) char *data; { float power[NUMTONES],thresh,maxpower; int on[NUMTONES],on_count; int bcount, rcount, ccount; int row, col, b1, b2, i; int r[4],c[4],b[8]; static int MFmode=0; calc_power(data,power); for(i=0, maxpower=0.0; i<NUMTONES;i++) if(power[i] > maxpower) maxpower = power[i]; /* for(i=0;i<NUMTONES;i++) printf("%f, ",power[i]); printf("\n"); */ if(maxpower < THRESH) /* silence? */ return(DSIL); thresh = RANGE * maxpower; /* allowable range of powers */ for(i=0, on_count=0; i<NUMTONES; i++) { if(power[i] > thresh) { on[i] = 1; on_count ++; } else on[i] = 0; } /* printf("%4d: ",on_count); for(i=0;i<NUMTONES;i++) putchar('0' + on[i]); printf("\n"); */ if(on_count == 1) { if(on[B7]) return(D24); if(on[B8]) return(D26); return(-1); } if(on_count == 2) { if(on[X1] && on[X2]) return(DDT); if(on[X2] && on[X3]) return(DRING); if(on[X3] && on[X4]) return(DBUSY); b[0]= on[B1]; b[1]= on[B2]; b[2]= on[B3]; b[3]= on[B4]; b[4]= on[B5]; b[5]= on[B6]; b[6]= on[B7]; b[7]= on[B8]; c[0]= on[C1]; c[1]= on[C2]; c[2]= on[C3]; c[3]= on[C4]; r[0]= on[R1]; r[1]= on[R2]; r[2]= on[R3]; r[3]= on[R4]; for(i=0, bcount=0; i<8; i++) { if(b[i]) { bcount++; b2 = b1; b1 = i; } } for(i=0, rcount=0; i<4; i++) { if(r[i]) { rcount++; row = i; } } for(i=0, ccount=0; i<4; i++) { if(c[i]) { ccount++; col = i; } } if(rcount==1 && ccount==1) { /* DTMF */ if(col == 3) /* A,B,C,D */ return(DA + row); else { if(row == 3 && col == 0 ) return(DSTAR); if(row == 3 && col == 2 ) return(DPND); if(row == 3) return(D0); if(row == 0 && col == 2) { /* DTMF 3 conflicts with MF 7 */ if(!MFmode) return(D3); } else return(D1 + col + row*3); } } if(bcount == 2) { /* MF */ /* b1 has upper number, b2 has lower */ switch(b1) { case 7: return( (b2==6)? D2426: -1); case 6: return(-1); case 5: if(b2==2 || b2==3) /* KP */ MFmode=1; if(b2==4) /* ST */ MFmode=0; return(DC11 + b2); /* MF 7 conflicts with DTMF 3, but if we made it * here then DTMF 3 was already tested for */ case 4: return( (b2==3)? D0: D7 + b2); case 3: return(D4 + b2); case 2: return(D2 + b2); case 1: return(D1); } } return(-1); } if(on_count == 0) return(DSIL); return(-1); } read_frame(fd,buf) int fd; char *buf; { int i,x; for(i=0; i<N; ) { x = read(fd, &buf[i], N-i); if(x <= 0) return(0); i += x; } return(1); } /* * read in frames, output the decoded * results */ dtmf_to_ascii(fd1, fd2) int fd1; FILE *fd2; { int x,last= DSIL; char frame[N+5]; int silence_time; while(read_frame(fd1, frame)) { x = decode(frame); /* if(x== -1) putchar('-'); if(x==DSIL) putchar(' '); if(x!=DSIL && x!=-1) putchar('a' + x); fflush(stdout); continue; */ if(x >= 0) { if(x == DSIL) silence_time += (silence_time>=0)?1:0 ; else silence_time= 0; if(silence_time == FLUSH_TIME) { fputs("\n",fd2); silence_time= -1; /* stop counting */ } if(x != DSIL && x != last && (last == DSIL || last==D24 || last == D26 || last == D2426 || last == DDT || last == DBUSY || last == DRING) ) { fputs(dtran[x], fd2); #ifndef NOFLUSH fflush(fd2); #endif } last = x; } } fputs("\n",fd2); } main(argc,argv) int argc; char **argv; { FILE *output; int input; input = 0; output = stdout; switch(argc) { case 1: break; case 3: output = fopen(argv[2],"w"); if(!output) { perror(argv[2]); return(-1); } /* fall through */ case 2: input = open(argv[1],0); if(input < 0) { perror(argv[1]); return(-1); } break; default: fprintf(stderr,"usage: %s [input [output]]\n",argv[0]); return(-1); } dtmf_to_ascii(input,output); fputs("Done.\n",output); return(0); }


Comment on Re: Parsing touch tone input?
Download Code
Re: Re: Parsing touch tone input?
by Anonymous Monk on Jul 20, 2002 at 21:58 UTC
    and here is detect.h
    /* * * goertzel aglorithm, find the power of different * frequencies in an N point DFT. * * ftone/fsample = k/N * k and N are integers. fsample is 8000 (8khz) * this means the *maximum* frequency resolution * is fsample/N (each step in k corresponds to a * step of fsample/N hz in ftone) * * N was chosen to minimize the sum of the K errors for * all the tones detected... here are the results : * * Best N is 240, with the sum of all errors = 3.030002 * freq freq actual k kactual kerr * ---- ------------ ------ ------- ----- * 350 (366.66667) 10.500 (11) 0.500 * 440 (433.33333) 13.200 (13) 0.200 * 480 (466.66667) 14.400 (14) 0.400 * 620 (633.33333) 18.600 (19) 0.400 * 697 (700.00000) 20.910 (21) 0.090 * 700 (700.00000) 21.000 (21) 0.000 * 770 (766.66667) 23.100 (23) 0.100 * 852 (866.66667) 25.560 (26) 0.440 * 900 (900.00000) 27.000 (27) 0.000 * 941 (933.33333) 28.230 (28) 0.230 * 1100 (1100.00000) 33.000 (33) 0.000 * 1209 (1200.00000) 36.270 (36) 0.270 * 1300 (1300.00000) 39.000 (39) 0.000 * 1336 (1333.33333) 40.080 (40) 0.080 **** I took out 1477.. too close to 1500 * 1477 (1466.66667) 44.310 (44) 0.310 **** * 1500 (1500.00000) 45.000 (45) 0.000 * 1633 (1633.33333) 48.990 (49) 0.010 * 1700 (1700.00000) 51.000 (51) 0.000 * 2400 (2400.00000) 72.000 (72) 0.000 * 2600 (2600.00000) 78.000 (78) 0.000 * * notice, 697 and 700hz are indestinguishable (same K) * all other tones have a seperate k value. * these two tones must be treated as identical for our * analysis. * * The worst tones to detect are 350 (error = 0.5, * detet 367 hz) and 852 (error = 0.44, detect 867hz). * all others are very close. * */ #define FSAMPLE 8000 #define N 240 int k[] = { 11, 13, 14, 19, 21, 23, 26, 27, 28, 33, 36, 39, 40, /*44,*/ 45, 49, 51, 72, 78, }; /* coefficients for above k's as: * 2 * cos( 2*pi* k/N ) */ float coef[] = { 1.917639, 1.885283, 1.867161, 1.757634, 1.705280, 1.648252, 1.554292, 1.520812, 1.486290, 1.298896, 1.175571, 1.044997, 1.000000, /* 0.813473,*/ 0.765367, 0.568031, 0.466891, -0.618034, -0.907981, }; #define X1 0 /* 350 dialtone */ #define X2 1 /* 440 ring, dialtone */ #define X3 2 /* 480 ring, busy */ #define X4 3 /* 620 busy */ #define R1 4 /* 697, dtmf row 1 */ #define R2 5 /* 770, dtmf row 2 */ #define R3 6 /* 852, dtmf row 3 */ #define R4 8 /* 941, dtmf row 4 */ #define C1 10 /* 1209, dtmf col 1 */ #define C2 12 /* 1336, dtmf col 2 */ #define C3 13 /* 1477, dtmf col 3 */ #define C4 14 /* 1633, dtmf col 4 */ #define B1 4 /* 700, blue box 1 */ #define B2 7 /* 900, bb 2 */ #define B3 9 /* 1100, bb 3 */ #define B4 11 /* 1300, bb4 */ #define B5 13 /* 1500, bb5 */ #define B6 15 /* 1700, bb6 */ #define B7 16 /* 2400, bb7 */ #define B8 17 /* 2600, bb8 */ #define NUMTONES 18 /* values returned by detect * 0-9 DTMF 0 through 9 or MF 0-9 * 10-11 DTMF *, # * 12-15 DTMF A,B,C,D * 16-20 MF last column: C11, C12, KP1, KP2, ST * 21 2400 * 22 2600 * 23 2400 + 2600 * 24 DIALTONE * 25 RING * 26 BUSY * 27 silence * -1 invalid */ #define D0 0 #define D1 1 #define D2 2 #define D3 3 #define D4 4 #define D5 5 #define D6 6 #define D7 7 #define D8 8 #define D9 9 #define DSTAR 10 #define DPND 11 #define DA 12 #define DB 13 #define DC 14 #define DD 15 #define DC11 16 #define DC12 17 #define DKP1 18 #define DKP2 19 #define DST 20 #define D24 21 #define D26 22 #define D2426 23 #define DDT 24 #define DRING 25 #define DBUSY 26 #define DSIL 27 /* translation of above codes into text */ char *dtran[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "*", "#", "A", "B", "C", "D", "+C11 ", "+C12 ", " KP1+", " KP2+", "+ST ", " 2400 ", " 2600 ", " 2400+2600 ", " DIALTONE ", " RING ", " BUSY ","" }; #define RANGE 0.1 /* any thing higher than RANGE*peak is "o +n" */ #define THRESH 100.0 /* minimum level for the loudest tone */ #define FLUSH_TIME 100 /* 100 frames = 3 seconds */

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://183683]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others browsing the Monastery: (4)
As of 2015-07-04 00:26 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The top three priorities of my open tasks are (in descending order of likelihood to be worked on) ...









    Results (57 votes), past polls