2008-07-27 03:56:38 +04:00
/*
* DTMF decoder .
*
* Copyright by Andreas Eversberg ( jolly @ eversberg . eu )
* based on different decoders such as ISDN4Linux
*
* This software may be used and distributed according to the terms
* of the GNU General Public License , incorporated herein by reference .
*
*/
# include <linux/mISDNif.h>
# include <linux/mISDNdsp.h>
# include "core.h"
# include "dsp.h"
# define NCOEFF 8 /* number of frequencies to be analyzed */
/* For DTMF recognition:
* 2 * cos ( 2 * PI * k / N ) precalculated for all k
*/
static u64 cos2pik [ NCOEFF ] =
{
/* k << 15 (source: hfc-4s/8s documentation (www.colognechip.de)) */
55960 , 53912 , 51402 , 48438 , 38146 , 32650 , 26170 , 18630
} ;
/* digit matrix */
static char dtmf_matrix [ 4 ] [ 4 ] =
{
{ ' 1 ' , ' 2 ' , ' 3 ' , ' A ' } ,
{ ' 4 ' , ' 5 ' , ' 6 ' , ' B ' } ,
{ ' 7 ' , ' 8 ' , ' 9 ' , ' C ' } ,
{ ' * ' , ' 0 ' , ' # ' , ' D ' }
} ;
/* dtmf detection using goertzel algorithm
* init function
*/
void dsp_dtmf_goertzel_init ( struct dsp * dsp )
{
dsp - > dtmf . size = 0 ;
dsp - > dtmf . lastwhat = ' \0 ' ;
dsp - > dtmf . lastdigit = ' \0 ' ;
dsp - > dtmf . count = 0 ;
}
/* check for hardware or software features
*/
void dsp_dtmf_hardware ( struct dsp * dsp )
{
int hardware = 1 ;
2009-05-22 15:04:58 +04:00
if ( ! dsp - > dtmf . enable )
return ;
2008-07-27 03:56:38 +04:00
if ( ! dsp - > features . hfc_dtmf )
hardware = 0 ;
/* check for volume change */
if ( dsp - > tx_volume ) {
if ( dsp_debug & DEBUG_DSP_DTMF )
printk ( KERN_DEBUG " %s dsp %s cannot do hardware DTMF, "
2012-02-20 07:52:38 +04:00
" because tx_volume is changed \n " ,
__func__ , dsp - > name ) ;
2008-07-27 03:56:38 +04:00
hardware = 0 ;
}
if ( dsp - > rx_volume ) {
if ( dsp_debug & DEBUG_DSP_DTMF )
printk ( KERN_DEBUG " %s dsp %s cannot do hardware DTMF, "
2012-02-20 07:52:38 +04:00
" because rx_volume is changed \n " ,
__func__ , dsp - > name ) ;
2008-07-27 03:56:38 +04:00
hardware = 0 ;
}
/* check if encryption is enabled */
if ( dsp - > bf_enable ) {
if ( dsp_debug & DEBUG_DSP_DTMF )
printk ( KERN_DEBUG " %s dsp %s cannot do hardware DTMF, "
2012-02-20 07:52:38 +04:00
" because encryption is enabled \n " ,
__func__ , dsp - > name ) ;
2008-07-27 03:56:38 +04:00
hardware = 0 ;
}
/* check if pipeline exists */
if ( dsp - > pipeline . inuse ) {
if ( dsp_debug & DEBUG_DSP_DTMF )
printk ( KERN_DEBUG " %s dsp %s cannot do hardware DTMF, "
2012-02-20 07:52:38 +04:00
" because pipeline exists. \n " ,
__func__ , dsp - > name ) ;
2008-07-27 03:56:38 +04:00
hardware = 0 ;
}
dsp - > dtmf . hardware = hardware ;
dsp - > dtmf . software = ! hardware ;
}
/*************************************************************
* calculate the coefficients of the given sample and decode *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* the given sample is decoded. if the sample is not long enough for a
* complete frame , the decoding is finished and continued with the next
* call of this function .
*
* the algorithm is very good for detection with a minimum of errors . i
* tested it allot . it even works with very short tones ( 40 ms ) . the only
* disadvantage is , that it doesn ' t work good with different volumes of both
* tones . this will happen , if accoustically coupled dialers are used .
2011-03-31 05:57:33 +04:00
* it sometimes detects tones during speech , which is normal for decoders .
2008-07-27 03:56:38 +04:00
* use sequences to given commands during calls .
*
* dtmf - points to a structure of the current dtmf state
* spl and len - the sample
* fmt - 0 = alaw , 1 = ulaw , 2 = coefficients from HFC DTMF hw - decoder
*/
u8
* dsp_dtmf_goertzel_decode ( struct dsp * dsp , u8 * data , int len , int fmt )
{
u8 what ;
int size ;
signed short * buf ;
s32 sk , sk1 , sk2 ;
int k , n , i ;
s32 * hfccoeff ;
s32 result [ NCOEFF ] , tresh , treshl ;
int lowgroup , highgroup ;
s64 cos2pik_ ;
dsp - > dtmf . digits [ 0 ] = ' \0 ' ;
/* Note: The function will loop until the buffer has not enough samples
* left to decode a full frame .
*/
again :
/* convert samples */
size = dsp - > dtmf . size ;
buf = dsp - > dtmf . buffer ;
switch ( fmt ) {
case 0 : /* alaw */
case 1 : /* ulaw */
while ( size < DSP_DTMF_NPOINTS & & len ) {
buf [ size + + ] = dsp_audio_law_to_s32 [ * data + + ] ;
len - - ;
}
break ;
case 2 : /* HFC coefficients */
default :
if ( len < 64 ) {
if ( len > 0 )
printk ( KERN_ERR " %s: coefficients have invalid "
2012-02-20 07:52:38 +04:00
" size. (is=%d < must=%d) \n " ,
__func__ , len , 64 ) ;
2008-07-27 03:56:38 +04:00
return dsp - > dtmf . digits ;
}
hfccoeff = ( s32 * ) data ;
for ( k = 0 ; k < NCOEFF ; k + + ) {
2012-02-20 07:52:38 +04:00
sk2 = ( * hfccoeff + + ) > > 4 ;
sk = ( * hfccoeff + + ) > > 4 ;
2008-07-27 03:56:38 +04:00
if ( sk > 32767 | | sk < - 32767 | | sk2 > 32767
| | sk2 < - 32767 )
printk ( KERN_WARNING
2012-02-20 07:52:38 +04:00
" DTMF-Detection overflow \n " ) ;
2008-07-27 03:56:38 +04:00
/* compute |X(k)|**2 */
result [ k ] =
2012-02-20 07:52:38 +04:00
( sk * sk ) -
( ( ( cos2pik [ k ] * sk ) > > 15 ) * sk2 ) +
( sk2 * sk2 ) ;
2008-07-27 03:56:38 +04:00
}
data + = 64 ;
len - = 64 ;
goto coefficients ;
break ;
}
dsp - > dtmf . size = size ;
if ( size < DSP_DTMF_NPOINTS )
return dsp - > dtmf . digits ;
dsp - > dtmf . size = 0 ;
/* now we have a full buffer of signed long samples - we do goertzel */
for ( k = 0 ; k < NCOEFF ; k + + ) {
sk = 0 ;
sk1 = 0 ;
sk2 = 0 ;
buf = dsp - > dtmf . buffer ;
cos2pik_ = cos2pik [ k ] ;
for ( n = 0 ; n < DSP_DTMF_NPOINTS ; n + + ) {
2012-02-20 07:52:38 +04:00
sk = ( ( cos2pik_ * sk1 ) > > 15 ) - sk2 + ( * buf + + ) ;
2008-07-27 03:56:38 +04:00
sk2 = sk1 ;
sk1 = sk ;
}
sk > > = 8 ;
sk2 > > = 8 ;
if ( sk > 32767 | | sk < - 32767 | | sk2 > 32767 | | sk2 < - 32767 )
printk ( KERN_WARNING " DTMF-Detection overflow \n " ) ;
/* compute |X(k)|**2 */
result [ k ] =
( sk * sk ) -
( ( ( cos2pik [ k ] * sk ) > > 15 ) * sk2 ) +
( sk2 * sk2 ) ;
}
/* our (squared) coefficients have been calculated, we need to process
* them .
*/
coefficients :
tresh = 0 ;
for ( i = 0 ; i < NCOEFF ; i + + ) {
if ( result [ i ] < 0 )
result [ i ] = 0 ;
if ( result [ i ] > dsp - > dtmf . treshold ) {
if ( result [ i ] > tresh )
tresh = result [ i ] ;
}
}
if ( tresh = = 0 ) {
what = 0 ;
goto storedigit ;
}
2012-04-24 06:51:51 +04:00
if ( dsp_debug & DEBUG_DSP_DTMFCOEFF ) {
s32 tresh_100 = tresh / 100 ;
if ( tresh_100 = = 0 ) {
tresh_100 = 1 ;
printk ( KERN_DEBUG
" tresh(%d) too small set tresh/100 to 1 \n " ,
tresh ) ;
}
2008-07-27 03:56:38 +04:00
printk ( KERN_DEBUG " a %3d %3d %3d %3d %3d %3d %3d %3d "
2012-02-20 07:52:38 +04:00
" tr:%3d r %3d %3d %3d %3d %3d %3d %3d %3d \n " ,
result [ 0 ] / 10000 , result [ 1 ] / 10000 , result [ 2 ] / 10000 ,
result [ 3 ] / 10000 , result [ 4 ] / 10000 , result [ 5 ] / 10000 ,
result [ 6 ] / 10000 , result [ 7 ] / 10000 , tresh / 10000 ,
2012-04-24 06:51:51 +04:00
result [ 0 ] / ( tresh_100 ) , result [ 1 ] / ( tresh_100 ) ,
result [ 2 ] / ( tresh_100 ) , result [ 3 ] / ( tresh_100 ) ,
result [ 4 ] / ( tresh_100 ) , result [ 5 ] / ( tresh_100 ) ,
result [ 6 ] / ( tresh_100 ) , result [ 7 ] / ( tresh_100 ) ) ;
}
2008-07-27 03:56:38 +04:00
/* calc digit (lowgroup/highgroup) */
lowgroup = - 1 ;
highgroup = - 1 ;
treshl = tresh > > 3 ; /* tones which are not on, must be below 9 dB */
tresh = tresh > > 2 ; /* touchtones must match within 6 dB */
for ( i = 0 ; i < NCOEFF ; i + + ) {
if ( result [ i ] < treshl )
continue ; /* ignore */
if ( result [ i ] < tresh ) {
lowgroup = - 1 ;
highgroup = - 1 ;
2011-03-31 05:57:33 +04:00
break ; /* noise in between */
2008-07-27 03:56:38 +04:00
}
/* good level found. This is allowed only one time per group */
2012-02-20 07:52:38 +04:00
if ( i < NCOEFF / 2 ) {
2008-07-27 03:56:38 +04:00
/* lowgroup */
if ( lowgroup > = 0 ) {
/* Bad. Another tone found. */
lowgroup = - 1 ;
break ;
} else
lowgroup = i ;
} else {
/* higroup */
if ( highgroup > = 0 ) {
/* Bad. Another tone found. */
highgroup = - 1 ;
break ;
} else
2012-02-20 07:52:38 +04:00
highgroup = i - ( NCOEFF / 2 ) ;
2008-07-27 03:56:38 +04:00
}
}
/* get digit or null */
what = 0 ;
if ( lowgroup > = 0 & & highgroup > = 0 )
what = dtmf_matrix [ lowgroup ] [ highgroup ] ;
storedigit :
if ( what & & ( dsp_debug & DEBUG_DSP_DTMF ) )
printk ( KERN_DEBUG " DTMF what: %c \n " , what ) ;
if ( dsp - > dtmf . lastwhat ! = what )
dsp - > dtmf . count = 0 ;
/* the tone (or no tone) must remain 3 times without change */
if ( dsp - > dtmf . count = = 2 ) {
if ( dsp - > dtmf . lastdigit ! = what ) {
dsp - > dtmf . lastdigit = what ;
if ( what ) {
if ( dsp_debug & DEBUG_DSP_DTMF )
printk ( KERN_DEBUG " DTMF digit: %c \n " ,
2012-02-20 07:52:38 +04:00
what ) ;
if ( ( strlen ( dsp - > dtmf . digits ) + 1 )
< sizeof ( dsp - > dtmf . digits ) ) {
2008-07-27 03:56:38 +04:00
dsp - > dtmf . digits [ strlen (
2012-02-20 07:52:38 +04:00
dsp - > dtmf . digits ) + 1 ] = ' \0 ' ;
2008-07-27 03:56:38 +04:00
dsp - > dtmf . digits [ strlen (
2012-02-20 07:52:38 +04:00
dsp - > dtmf . digits ) ] = what ;
2008-07-27 03:56:38 +04:00
}
}
}
} else
dsp - > dtmf . count + + ;
dsp - > dtmf . lastwhat = what ;
goto again ;
}