Forums

DTMF with Goertzel algoritm

Started by O-Zone August 24, 2004
Hi all,
i'm working on a digital auto.responder with voice modems. I need to
recognize DTMF so i use Goertzel Algoritm to detect it. Here's the code:

/* TestDTMF
 * Copyright (C) 2004 Pinassi Michele
 *
 * This software is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
/* ========================================================================== */
/* This simple program recognize, from a 8-bit UNSIGNED PCM audio file,       */
/* frequencies specified by user.                                             */
/* Piece of code modified from Phrack vol.50                                  */
/* (C) 2004 O-Zone <o-zone@zerozone.it>                                       */
/* ========================================================================== */

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

#define RANGE  0.1           /* any thing higher than RANGE*peak is "on" */
#define THRESH 100.0         /* minimum level for the loudest tone */

#define FSAMPLE  11025
#define NUM_FREQ 9

#define ROW1 0
#define ROW2 1
#define ROW3 2
#define ROW4 3

#define COL1 4
#define COL2 5
#define COL3 6
#define COL4 7

int freq[] = {	697, 770, 852, 941, 
		1209, 1336, 1477, 1633, 
		1800 };

float coef[NUM_FREQ];

/* coefficients for above k's as:
 *   2 * cos( 2*pi* k/N )
 */

#define PI 	3.141592

int N;
char last;

void DTMF_MakeCoeff() {
    int c;
    float k;
    for(c=0;c<NUM_FREQ;c++) {
	k = (freq[c] * N) / FSAMPLE;
	coef[c] = 2 * cos(2 * PI * (k / N));	
/*	printf("Coeff[%d] = %g\n",freq[c],coef[c]);  */
    }
}

int DTMF_CalcPower(unsigned char *data,float *power) {
  float u0[NUM_FREQ],u1[NUM_FREQ],t,in;
  int i,j;
  
  for(j=0; j<NUM_FREQ; j++) {
    u0[j] = 0.0;
    u1[j] = 0.0;
  }
  for(i=0; i<N; i++) {   /* feedback */
    in = ((int)data[i] - 128) / 128.0;
    for(j=0; j<NUM_FREQ; j++) {
      t = u0[j];
      u0[j] = in + coef[j] * u0[j] - u1[j];
      u1[j] = t;
    }
  }
  for(j=0; j<NUM_FREQ; j++)   /* feedforward */
    power[j] = u0[j] * u0[j] + u1[j] * u1[j] - coef[j] * u0[j] * u1[j]; 
  return(0);
}

int DTMF_Decode(char *data) {
    float power[NUM_FREQ],thresh,maxpower;
    int on[NUM_FREQ],on_count,i;
      
    DTMF_CalcPower(data,power);
  
    for(i=0, maxpower=0.0; i<NUM_FREQ;i++) {
	if(power[i] > maxpower) {
    	    maxpower = power[i]; 
	}	    
    }
    if(maxpower < THRESH) { /* silence? */ 
	return(0);
    }    
    thresh = RANGE * maxpower;    /* allowable range of powers */
/*    printf("Threshold: %f\n",thresh); */
    for(i=0, on_count=0; i<NUM_FREQ; i++) {
	if(power[i] > thresh) {
/*	    printf("Power[%d] @ %dHz= %f\n",i,freq[i],power[i]); */
    	    on[i] = 1;
    	    on_count ++;
	} else {
    	    on[i] = 0;
	}	    
    }    
    /* Adesso distingui che roba e' arrivata ! */
    if(on[ROW1] == 1) {
	if((last != '1') && (on[COL1] == 1)) return '1';
	if((last != '2') && (on[COL2] == 1)) return '2';
	if((last != '3') && (on[COL3] == 1)) return '3';
	if((last != 'a') && (on[COL4] == 1)) return 'a';
    } else if(on[ROW2] == 1) {
	if((last != '4') && (on[COL1] == 1)) return '4';
	if((last != '5') && (on[COL2] == 1)) return '5';
	if((last != '6') && (on[COL3] == 1)) return '6';
	if((last != 'b') && (on[COL4] == 1)) return 'b';
    } else if(on[ROW3] == 1) {
	if((last != '7') && (on[COL1] == 1)) return '7';
	if((last != '8') && (on[COL2] == 1)) return '8';
	if((last != '9') && (on[COL3] == 1)) return '9';
	if((last != 'c') && (on[COL4] == 1)) return 'c';
    } else if(on[ROW4] == 1) {
	if((last != '*') && (on[COL1] == 1)) return '*';
	if((last != '0') && (on[COL2] == 1)) return '0';
	if((last != '#') && (on[COL3] == 1)) return '#';
	if((last != 'd') && (on[COL4] == 1)) return 'd';
    }	
    return 0;
}

int DTMF_ToAscii(char *Data)
{
    int x;
    char frame[N+5];

    for(x=0;x<N;x++) {
	frame[x] = (char)((float)Data[x] * 0.50);
    }
    
    x = DTMF_Decode(frame); 
    
    return x;
}

void main(int argc,char *argv[]) {
    FILE *pcmfile;
    char tempbuf[1000],dtmf;
    int ret,ok;
    
    if(argc < 2) {
	printf("I need the PCM file as first arg and N value for second !\n");
	exit(0);
    }
    DTMF_MakeCoeff();
    
    if(strlen(argv[2]) > 0) {
	N = atoi(argv[2]);
    } else {
	N = 300; /* Default N Value */
    }

    pcmfile = fopen(argv[1],"r");
    if(pcmfile == NULL) {
    	    printf("Cannot open file %s\n",argv[1]);
	    exit(0);
    }
    printf("Analyzing %s with N:%d\n",argv[1],N);
    while(!feof(pcmfile)) {
      ret = fread(tempbuf,N,1,pcmfile);	
      last = dtmf = DTMF_ToAscii(tempbuf);
      if(dtmf > 0) printf("%c ",dtmf);
    }
    fclose(pcmfile);
}

All seems to work *but* DTMF are not recognized very well ! Please note
that PCM are a 8 bit unsigned RAW data with 11.025Hz data rate. 

Someone can help me ? Advices ? better algorithms ?

Tnx !!! Oz


-- 
O-Zone ! No(C) 2004
MyPHOTOS Photo-BLOG >>> http://myphotos.zerozone.it <<< 

O-Zone schrieb:

> Hi all, > i'm working on a digital auto.responder with voice modems. I need to > recognize DTMF so i use Goertzel Algoritm to detect it. Here's the code: > > /* TestDTMF > * Copyright (C) 2004 Pinassi Michele > * > * This software is free software; you can redistribute it and/or > * modify it under the terms of the GNU Lesser General Public > * License as published by the Free Software Foundation; either > * version 2 of the License, or (at your option) any later version. > * > * This library is distributed in the hope that it will be useful, > * but WITHOUT ANY WARRANTY; without even the implied warranty of > * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > * Lesser General Public License for more details. > * > * You should have received a copy of the GNU Lesser General Public > * License along with this library; if not, write to the > * Free Software Foundation, Inc., 59 Temple Place - Suite 330, > * Boston, MA 02111-1307, USA. > */ > /* ========================================================================== */ > /* This simple program recognize, from a 8-bit UNSIGNED PCM audio file, */ > /* frequencies specified by user. */ > /* Piece of code modified from Phrack vol.50 */ > /* (C) 2004 O-Zone <o-zone@zerozone.it> */ > /* ========================================================================== */ > > #include <stdio.h> > #include <unistd.h> > #include <math.h> > > #define RANGE 0.1 /* any thing higher than RANGE*peak is "on" */ > #define THRESH 100.0 /* minimum level for the loudest tone */ > > #define FSAMPLE 11025 > #define NUM_FREQ 9 > > #define ROW1 0 > #define ROW2 1 > #define ROW3 2 > #define ROW4 3 > > #define COL1 4 > #define COL2 5 > #define COL3 6 > #define COL4 7 > > int freq[] = { 697, 770, 852, 941, > 1209, 1336, 1477, 1633, > 1800 }; > > float coef[NUM_FREQ]; > > /* coefficients for above k's as: > * 2 * cos( 2*pi* k/N ) > */ > > #define PI 3.141592 > > int N; > char last; > > void DTMF_MakeCoeff() { > int c; > float k; > for(c=0;c<NUM_FREQ;c++) { > k = (freq[c] * N) / FSAMPLE; > coef[c] = 2 * cos(2 * PI * (k / N)); > /* printf("Coeff[%d] = %g\n",freq[c],coef[c]); */ > } > } > > int DTMF_CalcPower(unsigned char *data,float *power) { > float u0[NUM_FREQ],u1[NUM_FREQ],t,in; > int i,j; > > for(j=0; j<NUM_FREQ; j++) { > u0[j] = 0.0; > u1[j] = 0.0; > } > for(i=0; i<N; i++) { /* feedback */ > in = ((int)data[i] - 128) / 128.0; > for(j=0; j<NUM_FREQ; j++) { > t = u0[j]; > u0[j] = in + coef[j] * u0[j] - u1[j]; > u1[j] = t; > } > } > for(j=0; j<NUM_FREQ; j++) /* feedforward */ > power[j] = u0[j] * u0[j] + u1[j] * u1[j] - coef[j] * u0[j] * u1[j]; > return(0); > } > > int DTMF_Decode(char *data) { > float power[NUM_FREQ],thresh,maxpower; > int on[NUM_FREQ],on_count,i; > > DTMF_CalcPower(data,power); > > for(i=0, maxpower=0.0; i<NUM_FREQ;i++) { > if(power[i] > maxpower) { > maxpower = power[i]; > } > } > if(maxpower < THRESH) { /* silence? */ > return(0); > } > thresh = RANGE * maxpower; /* allowable range of powers */ > /* printf("Threshold: %f\n",thresh); */ > for(i=0, on_count=0; i<NUM_FREQ; i++) { > if(power[i] > thresh) { > /* printf("Power[%d] @ %dHz= %f\n",i,freq[i],power[i]); */ > on[i] = 1; > on_count ++; > } else { > on[i] = 0; > } > } > /* Adesso distingui che roba e' arrivata ! */ > if(on[ROW1] == 1) { > if((last != '1') && (on[COL1] == 1)) return '1'; > if((last != '2') && (on[COL2] == 1)) return '2'; > if((last != '3') && (on[COL3] == 1)) return '3'; > if((last != 'a') && (on[COL4] == 1)) return 'a'; > } else if(on[ROW2] == 1) { > if((last != '4') && (on[COL1] == 1)) return '4'; > if((last != '5') && (on[COL2] == 1)) return '5'; > if((last != '6') && (on[COL3] == 1)) return '6'; > if((last != 'b') && (on[COL4] == 1)) return 'b'; > } else if(on[ROW3] == 1) { > if((last != '7') && (on[COL1] == 1)) return '7'; > if((last != '8') && (on[COL2] == 1)) return '8'; > if((last != '9') && (on[COL3] == 1)) return '9'; > if((last != 'c') && (on[COL4] == 1)) return 'c'; > } else if(on[ROW4] == 1) { > if((last != '*') && (on[COL1] == 1)) return '*'; > if((last != '0') && (on[COL2] == 1)) return '0'; > if((last != '#') && (on[COL3] == 1)) return '#'; > if((last != 'd') && (on[COL4] == 1)) return 'd'; > } > return 0; > } > > int DTMF_ToAscii(char *Data) > { > int x; > char frame[N+5]; > > for(x=0;x<N;x++) { > frame[x] = (char)((float)Data[x] * 0.50); > } > > x = DTMF_Decode(frame); > > return x; > } > > void main(int argc,char *argv[]) { > FILE *pcmfile; > char tempbuf[1000],dtmf; > int ret,ok; > > if(argc < 2) { > printf("I need the PCM file as first arg and N value for second !\n"); > exit(0); > } > DTMF_MakeCoeff(); > > if(strlen(argv[2]) > 0) { > N = atoi(argv[2]); > } else { > N = 300; /* Default N Value */ > } > > pcmfile = fopen(argv[1],"r"); > if(pcmfile == NULL) { > printf("Cannot open file %s\n",argv[1]); > exit(0); > } > printf("Analyzing %s with N:%d\n",argv[1],N); > while(!feof(pcmfile)) { > ret = fread(tempbuf,N,1,pcmfile); > last = dtmf = DTMF_ToAscii(tempbuf); > if(dtmf > 0) printf("%c ",dtmf); > } > fclose(pcmfile); > } > > All seems to work *but* DTMF are not recognized very well ! Please note > that PCM are a 8 bit unsigned RAW data with 11.025Hz data rate. > > Someone can help me ? Advices ? better algorithms ? > > Tnx !!! Oz > >
The g&ouml;rtzelfilter is implemented in the rigth way. There are some points how you can improve the detection of the tones: - Normalize your signalbuffer before you apply the filter. This will help you setting up the threshold within the DTMF_Decode-function - The max. energy of COL and ROW should not differ to much (~6dB) - If music is mixed with your DTMF signal, you should check the second harmonic too: {697, 770, 852, 941, 1209, 1336, 1477, 1633, 1800} *2 - Before you detect a new tone, make sure you have detected a pause Regards Roman
Hi,

First of all I could quickly see there is integer divisions and only
the result is stored in float number.

> > k = (freq[c] * N) / FSAMPLE;
I guess you need to take one sample input and go through line-by-line to remove all those bugs. The DSP algorithms are very sensitive to these types of small errors and one need to be very careful to handle all these issues. Anandh DSP consultant Kaliru Systems. Roman Arnet <user242@hotmail.com> wrote in message news:<cgfab5$3cv$1@newshispeed.ch>...
> O-Zone schrieb: > > > Hi all, > > i'm working on a digital auto.responder with voice modems. I need to > > recognize DTMF so i use Goertzel Algoritm to detect it. Here's the code: > > > > /* TestDTMF > > * Copyright (C) 2004 Pinassi Michele > > * > > * This software is free software; you can redistribute it and/or > > * modify it under the terms of the GNU Lesser General Public > > * License as published by the Free Software Foundation; either > > * version 2 of the License, or (at your option) any later version. > > * > > * This library is distributed in the hope that it will be useful, > > * but WITHOUT ANY WARRANTY; without even the implied warranty of > > * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > > * Lesser General Public License for more details. > > * > > * You should have received a copy of the GNU Lesser General Public > > * License along with this library; if not, write to the > > * Free Software Foundation, Inc., 59 Temple Place - Suite 330, > > * Boston, MA 02111-1307, USA. > > */ > > /* ========================================================================== */ > > /* This simple program recognize, from a 8-bit UNSIGNED PCM audio file, */ > > /* frequencies specified by user. */ > > /* Piece of code modified from Phrack vol.50 */ > > /* (C) 2004 O-Zone <o-zone@zerozone.it> */ > > /* ========================================================================== */ > > > > #include <stdio.h> > > #include <unistd.h> > > #include <math.h> > > > > #define RANGE 0.1 /* any thing higher than RANGE*peak is "on" */ > > #define THRESH 100.0 /* minimum level for the loudest tone */ > > > > #define FSAMPLE 11025 > > #define NUM_FREQ 9 > > > > #define ROW1 0 > > #define ROW2 1 > > #define ROW3 2 > > #define ROW4 3 > > > > #define COL1 4 > > #define COL2 5 > > #define COL3 6 > > #define COL4 7 > > > > int freq[] = { 697, 770, 852, 941, > > 1209, 1336, 1477, 1633, > > 1800 }; > > > > float coef[NUM_FREQ]; > > > > /* coefficients for above k's as: > > * 2 * cos( 2*pi* k/N ) > > */ > > > > #define PI 3.141592 > > > > int N; > > char last; > > > > void DTMF_MakeCoeff() { > > int c; > > float k; > > for(c=0;c<NUM_FREQ;c++) { > > k = (freq[c] * N) / FSAMPLE; > > coef[c] = 2 * cos(2 * PI * (k / N)); > > /* printf("Coeff[%d] = %g\n",freq[c],coef[c]); */ > > } > > } > > > > int DTMF_CalcPower(unsigned char *data,float *power) { > > float u0[NUM_FREQ],u1[NUM_FREQ],t,in; > > int i,j; > > > > for(j=0; j<NUM_FREQ; j++) { > > u0[j] = 0.0; > > u1[j] = 0.0; > > } > > for(i=0; i<N; i++) { /* feedback */ > > in = ((int)data[i] - 128) / 128.0; > > for(j=0; j<NUM_FREQ; j++) { > > t = u0[j]; > > u0[j] = in + coef[j] * u0[j] - u1[j]; > > u1[j] = t; > > } > > } > > for(j=0; j<NUM_FREQ; j++) /* feedforward */ > > power[j] = u0[j] * u0[j] + u1[j] * u1[j] - coef[j] * u0[j] * u1[j]; > > return(0); > > } > > > > int DTMF_Decode(char *data) { > > float power[NUM_FREQ],thresh,maxpower; > > int on[NUM_FREQ],on_count,i; > > > > DTMF_CalcPower(data,power); > > > > for(i=0, maxpower=0.0; i<NUM_FREQ;i++) { > > if(power[i] > maxpower) { > > maxpower = power[i]; > > } > > } > > if(maxpower < THRESH) { /* silence? */ > > return(0); > > } > > thresh = RANGE * maxpower; /* allowable range of powers */ > > /* printf("Threshold: %f\n",thresh); */ > > for(i=0, on_count=0; i<NUM_FREQ; i++) { > > if(power[i] > thresh) { > > /* printf("Power[%d] @ %dHz= %f\n",i,freq[i],power[i]); */ > > on[i] = 1; > > on_count ++; > > } else { > on[i] = 0; > > } > > } > > /* Adesso distingui che roba e' arrivata ! */ > > if(on[ROW1] == 1) { > > if((last != '1') && (on[COL1] == 1)) return '1'; > > if((last != '2') && (on[COL2] == 1)) return '2'; > > if((last != '3') && (on[COL3] == 1)) return '3'; > > if((last != 'a') && (on[COL4] == 1)) return 'a'; > > } else if(on[ROW2] == 1) { > > if((last != '4') && (on[COL1] == 1)) return '4'; > > if((last != '5') && (on[COL2] == 1)) return '5'; > > if((last != '6') && (on[COL3] == 1)) return '6'; > > if((last != 'b') && (on[COL4] == 1)) return 'b'; > > } else if(on[ROW3] == 1) { > > if((last != '7') && (on[COL1] == 1)) return '7'; > > if((last != '8') && (on[COL2] == 1)) return '8'; > > if((last != '9') && (on[COL3] == 1)) return '9'; > > if((last != 'c') && (on[COL4] == 1)) return 'c'; > > } else if(on[ROW4] == 1) { > > if((last != '*') && (on[COL1] == 1)) return '*'; > > if((last != '0') && (on[COL2] == 1)) return '0'; > > if((last != '#') && (on[COL3] == 1)) return '#'; > > if((last != 'd') && (on[COL4] == 1)) return 'd'; > > } > > return 0; > > } > > > > int DTMF_ToAscii(char *Data) > > { > > int x; > > char frame[N+5]; > > > > for(x=0;x<N;x++) { > > frame[x] = (char)((float)Data[x] * 0.50); > > } > > > > x = DTMF_Decode(frame); > > > > return x; > > } > > > > void main(int argc,char *argv[]) { > > FILE *pcmfile; > > char tempbuf[1000],dtmf; > > int ret,ok; > > > > if(argc < 2) { > > printf("I need the PCM file as first arg and N value for second !\n"); > > exit(0); > > } > > DTMF_MakeCoeff(); > > > > if(strlen(argv[2]) > 0) { > > N = atoi(argv[2]); > > } else { > > N = 300; /* Default N Value */ > > } > > > > pcmfile = fopen(argv[1],"r"); > > if(pcmfile == NULL) { > > printf("Cannot open file %s\n",argv[1]); > > exit(0); > > } > > printf("Analyzing %s with N:%d\n",argv[1],N); > > while(!feof(pcmfile)) { > > ret = fread(tempbuf,N,1,pcmfile); > > last = dtmf = DTMF_ToAscii(tempbuf); > > if(dtmf > 0) printf("%c ",dtmf); > > } > > fclose(pcmfile); > > } > > > > All seems to work *but* DTMF are not recognized very well ! Please note > > that PCM are a 8 bit unsigned RAW data with 11.025Hz data rate. > > > > Someone can help me ? Advices ? better algorithms ? > > > > Tnx !!! Oz > > > > > > The g&Atilde;&para;rtzelfilter is implemented in the rigth way. There are some points > how you can improve the detection of the tones: > > - Normalize your signalbuffer before you apply the filter. This will > help you setting up the threshold within the DTMF_Decode-function > - The max. energy of COL and ROW should not differ to much (~6dB) > - If music is mixed with your DTMF signal, you should check the second > harmonic too: {697, 770, 852, 941, 1209, 1336, 1477, 1633, 1800} *2 > - Before you detect a new tone, make sure you have detected a pause > > Regards Roman