| /* | |
| * 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; | |
| if (!dsp->dtmf.enable) | |
| return; | |
| 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, " | |
| "because tx_volume is changed\n", | |
| __func__, dsp->name); | |
| hardware = 0; | |
| } | |
| if (dsp->rx_volume) { | |
| if (dsp_debug & DEBUG_DSP_DTMF) | |
| printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " | |
| "because rx_volume is changed\n", | |
| __func__, dsp->name); | |
| 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, " | |
| "because encryption is enabled\n", | |
| __func__, dsp->name); | |
| 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, " | |
| "because pipeline exists.\n", | |
| __func__, dsp->name); | |
| 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 (40ms). 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. | |
| * it sometimes detects tones during speech, which is normal for decoders. | |
| * 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 " | |
| "size. (is=%d < must=%d)\n", | |
| __func__, len, 64); | |
| return dsp->dtmf.digits; | |
| } | |
| hfccoeff = (s32 *)data; | |
| for (k = 0; k < NCOEFF; k++) { | |
| sk2 = (*hfccoeff++) >> 4; | |
| sk = (*hfccoeff++) >> 4; | |
| 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); | |
| } | |
| 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++) { | |
| sk = ((cos2pik_ * sk1) >> 15) - sk2 + (*buf++); | |
| 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; | |
| } | |
| 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); | |
| } | |
| printk(KERN_DEBUG "a %3d %3d %3d %3d %3d %3d %3d %3d" | |
| " 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, | |
| 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)); | |
| } | |
| /* 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; | |
| break; /* noise in between */ | |
| } | |
| /* good level found. This is allowed only one time per group */ | |
| if (i < NCOEFF / 2) { | |
| /* 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 | |
| highgroup = i - (NCOEFF / 2); | |
| } | |
| } | |
| /* 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", | |
| what); | |
| if ((strlen(dsp->dtmf.digits) + 1) | |
| < sizeof(dsp->dtmf.digits)) { | |
| dsp->dtmf.digits[strlen( | |
| dsp->dtmf.digits) + 1] = '\0'; | |
| dsp->dtmf.digits[strlen( | |
| dsp->dtmf.digits)] = what; | |
| } | |
| } | |
| } | |
| } else | |
| dsp->dtmf.count++; | |
| dsp->dtmf.lastwhat = what; | |
| goto again; | |
| } |