18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * DTMF decoder. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright by Andreas Eversberg (jolly@eversberg.eu) 58c2ecf20Sopenharmony_ci * based on different decoders such as ISDN4Linux 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This software may be used and distributed according to the terms 88c2ecf20Sopenharmony_ci * of the GNU General Public License, incorporated herein by reference. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/mISDNif.h> 138c2ecf20Sopenharmony_ci#include <linux/mISDNdsp.h> 148c2ecf20Sopenharmony_ci#include "core.h" 158c2ecf20Sopenharmony_ci#include "dsp.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define NCOEFF 8 /* number of frequencies to be analyzed */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* For DTMF recognition: 208c2ecf20Sopenharmony_ci * 2 * cos(2 * PI * k / N) precalculated for all k 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_cistatic u64 cos2pik[NCOEFF] = 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci /* k << 15 (source: hfc-4s/8s documentation (www.colognechip.de)) */ 258c2ecf20Sopenharmony_ci 55960, 53912, 51402, 48438, 38146, 32650, 26170, 18630 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* digit matrix */ 298c2ecf20Sopenharmony_cistatic char dtmf_matrix[4][4] = 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci {'1', '2', '3', 'A'}, 328c2ecf20Sopenharmony_ci {'4', '5', '6', 'B'}, 338c2ecf20Sopenharmony_ci {'7', '8', '9', 'C'}, 348c2ecf20Sopenharmony_ci {'*', '0', '#', 'D'} 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* dtmf detection using goertzel algorithm 388c2ecf20Sopenharmony_ci * init function 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_civoid dsp_dtmf_goertzel_init(struct dsp *dsp) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci dsp->dtmf.size = 0; 438c2ecf20Sopenharmony_ci dsp->dtmf.lastwhat = '\0'; 448c2ecf20Sopenharmony_ci dsp->dtmf.lastdigit = '\0'; 458c2ecf20Sopenharmony_ci dsp->dtmf.count = 0; 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* check for hardware or software features 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_civoid dsp_dtmf_hardware(struct dsp *dsp) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci int hardware = 1; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (!dsp->dtmf.enable) 558c2ecf20Sopenharmony_ci return; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci if (!dsp->features.hfc_dtmf) 588c2ecf20Sopenharmony_ci hardware = 0; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci /* check for volume change */ 618c2ecf20Sopenharmony_ci if (dsp->tx_volume) { 628c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_DTMF) 638c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " 648c2ecf20Sopenharmony_ci "because tx_volume is changed\n", 658c2ecf20Sopenharmony_ci __func__, dsp->name); 668c2ecf20Sopenharmony_ci hardware = 0; 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci if (dsp->rx_volume) { 698c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_DTMF) 708c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " 718c2ecf20Sopenharmony_ci "because rx_volume is changed\n", 728c2ecf20Sopenharmony_ci __func__, dsp->name); 738c2ecf20Sopenharmony_ci hardware = 0; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci /* check if encryption is enabled */ 768c2ecf20Sopenharmony_ci if (dsp->bf_enable) { 778c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_DTMF) 788c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " 798c2ecf20Sopenharmony_ci "because encryption is enabled\n", 808c2ecf20Sopenharmony_ci __func__, dsp->name); 818c2ecf20Sopenharmony_ci hardware = 0; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci /* check if pipeline exists */ 848c2ecf20Sopenharmony_ci if (dsp->pipeline.inuse) { 858c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_DTMF) 868c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " 878c2ecf20Sopenharmony_ci "because pipeline exists.\n", 888c2ecf20Sopenharmony_ci __func__, dsp->name); 898c2ecf20Sopenharmony_ci hardware = 0; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci dsp->dtmf.hardware = hardware; 938c2ecf20Sopenharmony_ci dsp->dtmf.software = !hardware; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/************************************************************* 988c2ecf20Sopenharmony_ci * calculate the coefficients of the given sample and decode * 998c2ecf20Sopenharmony_ci *************************************************************/ 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/* the given sample is decoded. if the sample is not long enough for a 1028c2ecf20Sopenharmony_ci * complete frame, the decoding is finished and continued with the next 1038c2ecf20Sopenharmony_ci * call of this function. 1048c2ecf20Sopenharmony_ci * 1058c2ecf20Sopenharmony_ci * the algorithm is very good for detection with a minimum of errors. i 1068c2ecf20Sopenharmony_ci * tested it allot. it even works with very short tones (40ms). the only 1078c2ecf20Sopenharmony_ci * disadvantage is, that it doesn't work good with different volumes of both 1088c2ecf20Sopenharmony_ci * tones. this will happen, if accoustically coupled dialers are used. 1098c2ecf20Sopenharmony_ci * it sometimes detects tones during speech, which is normal for decoders. 1108c2ecf20Sopenharmony_ci * use sequences to given commands during calls. 1118c2ecf20Sopenharmony_ci * 1128c2ecf20Sopenharmony_ci * dtmf - points to a structure of the current dtmf state 1138c2ecf20Sopenharmony_ci * spl and len - the sample 1148c2ecf20Sopenharmony_ci * fmt - 0 = alaw, 1 = ulaw, 2 = coefficients from HFC DTMF hw-decoder 1158c2ecf20Sopenharmony_ci */ 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ciu8 1188c2ecf20Sopenharmony_ci*dsp_dtmf_goertzel_decode(struct dsp *dsp, u8 *data, int len, int fmt) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci u8 what; 1218c2ecf20Sopenharmony_ci int size; 1228c2ecf20Sopenharmony_ci signed short *buf; 1238c2ecf20Sopenharmony_ci s32 sk, sk1, sk2; 1248c2ecf20Sopenharmony_ci int k, n, i; 1258c2ecf20Sopenharmony_ci s32 *hfccoeff; 1268c2ecf20Sopenharmony_ci s32 result[NCOEFF], tresh, treshl; 1278c2ecf20Sopenharmony_ci int lowgroup, highgroup; 1288c2ecf20Sopenharmony_ci s64 cos2pik_; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci dsp->dtmf.digits[0] = '\0'; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* Note: The function will loop until the buffer has not enough samples 1338c2ecf20Sopenharmony_ci * left to decode a full frame. 1348c2ecf20Sopenharmony_ci */ 1358c2ecf20Sopenharmony_ciagain: 1368c2ecf20Sopenharmony_ci /* convert samples */ 1378c2ecf20Sopenharmony_ci size = dsp->dtmf.size; 1388c2ecf20Sopenharmony_ci buf = dsp->dtmf.buffer; 1398c2ecf20Sopenharmony_ci switch (fmt) { 1408c2ecf20Sopenharmony_ci case 0: /* alaw */ 1418c2ecf20Sopenharmony_ci case 1: /* ulaw */ 1428c2ecf20Sopenharmony_ci while (size < DSP_DTMF_NPOINTS && len) { 1438c2ecf20Sopenharmony_ci buf[size++] = dsp_audio_law_to_s32[*data++]; 1448c2ecf20Sopenharmony_ci len--; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci break; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci case 2: /* HFC coefficients */ 1498c2ecf20Sopenharmony_ci default: 1508c2ecf20Sopenharmony_ci if (len < 64) { 1518c2ecf20Sopenharmony_ci if (len > 0) 1528c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: coefficients have invalid " 1538c2ecf20Sopenharmony_ci "size. (is=%d < must=%d)\n", 1548c2ecf20Sopenharmony_ci __func__, len, 64); 1558c2ecf20Sopenharmony_ci return dsp->dtmf.digits; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci hfccoeff = (s32 *)data; 1588c2ecf20Sopenharmony_ci for (k = 0; k < NCOEFF; k++) { 1598c2ecf20Sopenharmony_ci sk2 = (*hfccoeff++) >> 4; 1608c2ecf20Sopenharmony_ci sk = (*hfccoeff++) >> 4; 1618c2ecf20Sopenharmony_ci if (sk > 32767 || sk < -32767 || sk2 > 32767 1628c2ecf20Sopenharmony_ci || sk2 < -32767) 1638c2ecf20Sopenharmony_ci printk(KERN_WARNING 1648c2ecf20Sopenharmony_ci "DTMF-Detection overflow\n"); 1658c2ecf20Sopenharmony_ci /* compute |X(k)|**2 */ 1668c2ecf20Sopenharmony_ci result[k] = 1678c2ecf20Sopenharmony_ci (sk * sk) - 1688c2ecf20Sopenharmony_ci (((cos2pik[k] * sk) >> 15) * sk2) + 1698c2ecf20Sopenharmony_ci (sk2 * sk2); 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci data += 64; 1728c2ecf20Sopenharmony_ci len -= 64; 1738c2ecf20Sopenharmony_ci goto coefficients; 1748c2ecf20Sopenharmony_ci break; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci dsp->dtmf.size = size; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (size < DSP_DTMF_NPOINTS) 1798c2ecf20Sopenharmony_ci return dsp->dtmf.digits; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci dsp->dtmf.size = 0; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* now we have a full buffer of signed long samples - we do goertzel */ 1848c2ecf20Sopenharmony_ci for (k = 0; k < NCOEFF; k++) { 1858c2ecf20Sopenharmony_ci sk = 0; 1868c2ecf20Sopenharmony_ci sk1 = 0; 1878c2ecf20Sopenharmony_ci sk2 = 0; 1888c2ecf20Sopenharmony_ci buf = dsp->dtmf.buffer; 1898c2ecf20Sopenharmony_ci cos2pik_ = cos2pik[k]; 1908c2ecf20Sopenharmony_ci for (n = 0; n < DSP_DTMF_NPOINTS; n++) { 1918c2ecf20Sopenharmony_ci sk = ((cos2pik_ * sk1) >> 15) - sk2 + (*buf++); 1928c2ecf20Sopenharmony_ci sk2 = sk1; 1938c2ecf20Sopenharmony_ci sk1 = sk; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci sk >>= 8; 1968c2ecf20Sopenharmony_ci sk2 >>= 8; 1978c2ecf20Sopenharmony_ci if (sk > 32767 || sk < -32767 || sk2 > 32767 || sk2 < -32767) 1988c2ecf20Sopenharmony_ci printk(KERN_WARNING "DTMF-Detection overflow\n"); 1998c2ecf20Sopenharmony_ci /* compute |X(k)|**2 */ 2008c2ecf20Sopenharmony_ci result[k] = 2018c2ecf20Sopenharmony_ci (sk * sk) - 2028c2ecf20Sopenharmony_ci (((cos2pik[k] * sk) >> 15) * sk2) + 2038c2ecf20Sopenharmony_ci (sk2 * sk2); 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* our (squared) coefficients have been calculated, we need to process 2078c2ecf20Sopenharmony_ci * them. 2088c2ecf20Sopenharmony_ci */ 2098c2ecf20Sopenharmony_cicoefficients: 2108c2ecf20Sopenharmony_ci tresh = 0; 2118c2ecf20Sopenharmony_ci for (i = 0; i < NCOEFF; i++) { 2128c2ecf20Sopenharmony_ci if (result[i] < 0) 2138c2ecf20Sopenharmony_ci result[i] = 0; 2148c2ecf20Sopenharmony_ci if (result[i] > dsp->dtmf.treshold) { 2158c2ecf20Sopenharmony_ci if (result[i] > tresh) 2168c2ecf20Sopenharmony_ci tresh = result[i]; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (tresh == 0) { 2218c2ecf20Sopenharmony_ci what = 0; 2228c2ecf20Sopenharmony_ci goto storedigit; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_DTMFCOEFF) { 2268c2ecf20Sopenharmony_ci s32 tresh_100 = tresh/100; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (tresh_100 == 0) { 2298c2ecf20Sopenharmony_ci tresh_100 = 1; 2308c2ecf20Sopenharmony_ci printk(KERN_DEBUG 2318c2ecf20Sopenharmony_ci "tresh(%d) too small set tresh/100 to 1\n", 2328c2ecf20Sopenharmony_ci tresh); 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci printk(KERN_DEBUG "a %3d %3d %3d %3d %3d %3d %3d %3d" 2358c2ecf20Sopenharmony_ci " tr:%3d r %3d %3d %3d %3d %3d %3d %3d %3d\n", 2368c2ecf20Sopenharmony_ci result[0] / 10000, result[1] / 10000, result[2] / 10000, 2378c2ecf20Sopenharmony_ci result[3] / 10000, result[4] / 10000, result[5] / 10000, 2388c2ecf20Sopenharmony_ci result[6] / 10000, result[7] / 10000, tresh / 10000, 2398c2ecf20Sopenharmony_ci result[0] / (tresh_100), result[1] / (tresh_100), 2408c2ecf20Sopenharmony_ci result[2] / (tresh_100), result[3] / (tresh_100), 2418c2ecf20Sopenharmony_ci result[4] / (tresh_100), result[5] / (tresh_100), 2428c2ecf20Sopenharmony_ci result[6] / (tresh_100), result[7] / (tresh_100)); 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci /* calc digit (lowgroup/highgroup) */ 2468c2ecf20Sopenharmony_ci lowgroup = -1; 2478c2ecf20Sopenharmony_ci highgroup = -1; 2488c2ecf20Sopenharmony_ci treshl = tresh >> 3; /* tones which are not on, must be below 9 dB */ 2498c2ecf20Sopenharmony_ci tresh = tresh >> 2; /* touchtones must match within 6 dB */ 2508c2ecf20Sopenharmony_ci for (i = 0; i < NCOEFF; i++) { 2518c2ecf20Sopenharmony_ci if (result[i] < treshl) 2528c2ecf20Sopenharmony_ci continue; /* ignore */ 2538c2ecf20Sopenharmony_ci if (result[i] < tresh) { 2548c2ecf20Sopenharmony_ci lowgroup = -1; 2558c2ecf20Sopenharmony_ci highgroup = -1; 2568c2ecf20Sopenharmony_ci break; /* noise in between */ 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci /* good level found. This is allowed only one time per group */ 2598c2ecf20Sopenharmony_ci if (i < NCOEFF / 2) { 2608c2ecf20Sopenharmony_ci /* lowgroup */ 2618c2ecf20Sopenharmony_ci if (lowgroup >= 0) { 2628c2ecf20Sopenharmony_ci /* Bad. Another tone found. */ 2638c2ecf20Sopenharmony_ci lowgroup = -1; 2648c2ecf20Sopenharmony_ci break; 2658c2ecf20Sopenharmony_ci } else 2668c2ecf20Sopenharmony_ci lowgroup = i; 2678c2ecf20Sopenharmony_ci } else { 2688c2ecf20Sopenharmony_ci /* higroup */ 2698c2ecf20Sopenharmony_ci if (highgroup >= 0) { 2708c2ecf20Sopenharmony_ci /* Bad. Another tone found. */ 2718c2ecf20Sopenharmony_ci highgroup = -1; 2728c2ecf20Sopenharmony_ci break; 2738c2ecf20Sopenharmony_ci } else 2748c2ecf20Sopenharmony_ci highgroup = i - (NCOEFF / 2); 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* get digit or null */ 2798c2ecf20Sopenharmony_ci what = 0; 2808c2ecf20Sopenharmony_ci if (lowgroup >= 0 && highgroup >= 0) 2818c2ecf20Sopenharmony_ci what = dtmf_matrix[lowgroup][highgroup]; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistoredigit: 2848c2ecf20Sopenharmony_ci if (what && (dsp_debug & DEBUG_DSP_DTMF)) 2858c2ecf20Sopenharmony_ci printk(KERN_DEBUG "DTMF what: %c\n", what); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (dsp->dtmf.lastwhat != what) 2888c2ecf20Sopenharmony_ci dsp->dtmf.count = 0; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci /* the tone (or no tone) must remain 3 times without change */ 2918c2ecf20Sopenharmony_ci if (dsp->dtmf.count == 2) { 2928c2ecf20Sopenharmony_ci if (dsp->dtmf.lastdigit != what) { 2938c2ecf20Sopenharmony_ci dsp->dtmf.lastdigit = what; 2948c2ecf20Sopenharmony_ci if (what) { 2958c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_DTMF) 2968c2ecf20Sopenharmony_ci printk(KERN_DEBUG "DTMF digit: %c\n", 2978c2ecf20Sopenharmony_ci what); 2988c2ecf20Sopenharmony_ci if ((strlen(dsp->dtmf.digits) + 1) 2998c2ecf20Sopenharmony_ci < sizeof(dsp->dtmf.digits)) { 3008c2ecf20Sopenharmony_ci dsp->dtmf.digits[strlen( 3018c2ecf20Sopenharmony_ci dsp->dtmf.digits) + 1] = '\0'; 3028c2ecf20Sopenharmony_ci dsp->dtmf.digits[strlen( 3038c2ecf20Sopenharmony_ci dsp->dtmf.digits)] = what; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci } else 3088c2ecf20Sopenharmony_ci dsp->dtmf.count++; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci dsp->dtmf.lastwhat = what; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci goto again; 3138c2ecf20Sopenharmony_ci} 314