comparison spandsp-0.0.3/spandsp-0.0.3/src/dtmf.c @ 5:f762bf195c4b

import spandsp-0.0.3
author Peter Meerwald <pmeerw@cosy.sbg.ac.at>
date Fri, 25 Jun 2010 16:00:21 +0200
parents
children
comparison
equal deleted inserted replaced
4:26cd8f1ef0b1 5:f762bf195c4b
1 /*
2 * SpanDSP - a series of DSP components for telephony
3 *
4 * dtmf.c - DTMF generation and detection.
5 *
6 * Written by Steve Underwood <steveu@coppice.org>
7 *
8 * Copyright (C) 2001-2003, 2005, 2006 Steve Underwood
9 *
10 * All rights reserved.
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License version 2, as
14 * published by the Free Software Foundation.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 *
25 * $Id: dtmf.c,v 1.11 2006/11/19 14:07:24 steveu Exp $
26 */
27
28 /*! \file dtmf.h */
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33
34 #include <inttypes.h>
35 #include <stdlib.h>
36 #if defined(HAVE_TGMATH_H)
37 #include <tgmath.h>
38 #endif
39 #if defined(HAVE_MATH_H)
40 #include <math.h>
41 #endif
42 #include <string.h>
43 #include <stdio.h>
44 #include <time.h>
45 #include <fcntl.h>
46
47 #include "spandsp/telephony.h"
48 #include "spandsp/tone_detect.h"
49 #include "spandsp/tone_generate.h"
50 #include "spandsp/dtmf.h"
51
52 #if !defined(M_PI)
53 /* C99 systems may not define M_PI */
54 #define M_PI 3.14159265358979323846264338327
55 #endif
56
57 //#define USE_3DNOW
58
59 #define ms_to_samples(t) (((t)*SAMPLE_RATE)/1000)
60
61 #define DTMF_DURATION ms_to_samples(70)
62 #define DTMF_PAUSE ms_to_samples(80)
63 #define DTMF_CYCLE (DTMF_DURATION + DTMF_PAUSE)
64
65 #define DTMF_THRESHOLD 8.0e7f
66 #define DTMF_NORMAL_TWIST 6.3f /* 8dB */
67 #define DTMF_REVERSE_TWIST 2.5f /* 4dB */
68 #define DTMF_RELATIVE_PEAK_ROW 6.3f /* 8dB */
69 #define DTMF_RELATIVE_PEAK_COL 6.3f /* 8dB */
70 #define DTMF_TO_TOTAL_ENERGY 42.0f
71
72 static const float dtmf_row[] =
73 {
74 697.0f, 770.0f, 852.0f, 941.0f
75 };
76 static const float dtmf_col[] =
77 {
78 1209.0f, 1336.0f, 1477.0f, 1633.0f
79 };
80
81 static const char dtmf_positions[] = "123A" "456B" "789C" "*0#D";
82
83 static goertzel_descriptor_t dtmf_detect_row[4];
84 static goertzel_descriptor_t dtmf_detect_col[4];
85
86 static int dtmf_tx_inited = FALSE;
87 static tone_gen_descriptor_t dtmf_digit_tones[16];
88
89 #if defined(USE_3DNOW)
90 static __inline__ void _dtmf_goertzel_update(goertzel_state_t *s,
91 float x[],
92 int samples)
93 {
94 int n;
95 float v;
96 int i;
97 float vv[16];
98
99 vv[4] = s[0].v2;
100 vv[5] = s[1].v2;
101 vv[6] = s[2].v2;
102 vv[7] = s[3].v2;
103 vv[8] = s[0].v3;
104 vv[9] = s[1].v3;
105 vv[10] = s[2].v3;
106 vv[11] = s[3].v3;
107 vv[12] = s[0].fac;
108 vv[13] = s[1].fac;
109 vv[14] = s[2].fac;
110 vv[15] = s[3].fac;
111
112 //v1 = s->v2;
113 //s->v2 = s->v3;
114 //s->v3 = s->fac*s->v2 - v1 + x[0];
115
116 __asm__ __volatile__ (
117 " femms;\n"
118
119 " movq 16(%%edx),%%mm2;\n"
120 " movq 24(%%edx),%%mm3;\n"
121 " movq 32(%%edx),%%mm4;\n"
122 " movq 40(%%edx),%%mm5;\n"
123 " movq 48(%%edx),%%mm6;\n"
124 " movq 56(%%edx),%%mm7;\n"
125
126 " jmp 1f;\n"
127 " .align 32;\n"
128
129 " 1: ;\n"
130 " prefetch (%%eax);\n"
131 " movq %%mm3,%%mm1;\n"
132 " movq %%mm2,%%mm0;\n"
133 " movq %%mm5,%%mm3;\n"
134 " movq %%mm4,%%mm2;\n"
135
136 " pfmul %%mm7,%%mm5;\n"
137 " pfmul %%mm6,%%mm4;\n"
138 " pfsub %%mm1,%%mm5;\n"
139 " pfsub %%mm0,%%mm4;\n"
140
141 " movq (%%eax),%%mm0;\n"
142 " movq %%mm0,%%mm1;\n"
143 " punpckldq %%mm0,%%mm1;\n"
144 " add $4,%%eax;\n"
145 " pfadd %%mm1,%%mm5;\n"
146 " pfadd %%mm1,%%mm4;\n"
147
148 " dec %%ecx;\n"
149
150 " jnz 1b;\n"
151
152 " movq %%mm2,16(%%edx);\n"
153 " movq %%mm3,24(%%edx);\n"
154 " movq %%mm4,32(%%edx);\n"
155 " movq %%mm5,40(%%edx);\n"
156
157 " femms;\n"
158 :
159 : "c" (samples), "a" (x), "d" (vv)
160 : "memory", "eax", "ecx");
161
162 s[0].v2 = vv[4];
163 s[1].v2 = vv[5];
164 s[2].v2 = vv[6];
165 s[3].v2 = vv[7];
166 s[0].v3 = vv[8];
167 s[1].v3 = vv[9];
168 s[2].v3 = vv[10];
169 s[3].v3 = vv[11];
170 }
171 #endif
172 /*- End of function --------------------------------------------------------*/
173
174 int dtmf_rx(dtmf_rx_state_t *s, const int16_t amp[], int samples)
175 {
176 float row_energy[4];
177 float col_energy[4];
178 float famp;
179 float v1;
180 int i;
181 int j;
182 int sample;
183 int best_row;
184 int best_col;
185 int limit;
186 uint8_t hit;
187
188 hit = 0;
189 for (sample = 0; sample < samples; sample = limit)
190 {
191 /* The block length is optimised to meet the DTMF specs. */
192 if ((samples - sample) >= (102 - s->current_sample))
193 limit = sample + (102 - s->current_sample);
194 else
195 limit = samples;
196 #if defined(USE_3DNOW)
197 _dtmf_goertzel_update(s->row_out, amp + sample, limit - sample);
198 _dtmf_goertzel_update(s->col_out, amp + sample, limit - sample);
199 #else
200 /* The following unrolled loop takes only 35% (rough estimate) of the
201 time of a rolled loop on the machine on which it was developed */
202 for (j = sample; j < limit; j++)
203 {
204 famp = amp[j];
205 if (s->filter_dialtone)
206 {
207 /* Sharp notches applied at 350Hz and 440Hz - the two common dialtone frequencies.
208 These are rather high Q, to achieve the required narrowness, without using lots of
209 sections. */
210 v1 = 0.98356f*famp + 1.8954426f*s->z350_1 - 0.9691396f*s->z350_2;
211 famp = v1 - 1.9251480f*s->z350_1 + s->z350_2;
212 s->z350_2 = s->z350_1;
213 s->z350_1 = v1;
214
215 v1 = 0.98456f*famp + 1.8529543f*s->z440_1 - 0.9691396f*s->z440_2;
216 famp = v1 - 1.8819938f*s->z440_1 + s->z440_2;
217 s->z440_2 = s->z440_1;
218 s->z440_1 = v1;
219 }
220 s->energy += famp*famp;
221 /* With GCC 2.95, the following unrolled code seems to take about 35%
222 (rough estimate) as long as a neat little 0-3 loop */
223 v1 = s->row_out[0].v2;
224 s->row_out[0].v2 = s->row_out[0].v3;
225 s->row_out[0].v3 = s->row_out[0].fac*s->row_out[0].v2 - v1 + famp;
226
227 v1 = s->col_out[0].v2;
228 s->col_out[0].v2 = s->col_out[0].v3;
229 s->col_out[0].v3 = s->col_out[0].fac*s->col_out[0].v2 - v1 + famp;
230
231 v1 = s->row_out[1].v2;
232 s->row_out[1].v2 = s->row_out[1].v3;
233 s->row_out[1].v3 = s->row_out[1].fac*s->row_out[1].v2 - v1 + famp;
234
235 v1 = s->col_out[1].v2;
236 s->col_out[1].v2 = s->col_out[1].v3;
237 s->col_out[1].v3 = s->col_out[1].fac*s->col_out[1].v2 - v1 + famp;
238
239 v1 = s->row_out[2].v2;
240 s->row_out[2].v2 = s->row_out[2].v3;
241 s->row_out[2].v3 = s->row_out[2].fac*s->row_out[2].v2 - v1 + famp;
242
243 v1 = s->col_out[2].v2;
244 s->col_out[2].v2 = s->col_out[2].v3;
245 s->col_out[2].v3 = s->col_out[2].fac*s->col_out[2].v2 - v1 + famp;
246
247 v1 = s->row_out[3].v2;
248 s->row_out[3].v2 = s->row_out[3].v3;
249 s->row_out[3].v3 = s->row_out[3].fac*s->row_out[3].v2 - v1 + famp;
250
251 v1 = s->col_out[3].v2;
252 s->col_out[3].v2 = s->col_out[3].v3;
253 s->col_out[3].v3 = s->col_out[3].fac*s->col_out[3].v2 - v1 + famp;
254 }
255 #endif
256 s->current_sample += (limit - sample);
257 if (s->current_sample < 102)
258 continue;
259
260 /* We are at the end of a DTMF detection block */
261 /* Find the peak row and the peak column */
262 row_energy[0] = goertzel_result(&s->row_out[0]);
263 best_row = 0;
264 col_energy[0] = goertzel_result(&s->col_out[0]);
265 best_col = 0;
266
267 for (i = 1; i < 4; i++)
268 {
269 row_energy[i] = goertzel_result(&s->row_out[i]);
270 if (row_energy[i] > row_energy[best_row])
271 best_row = i;
272 col_energy[i] = goertzel_result(&s->col_out[i]);
273 if (col_energy[i] > col_energy[best_col])
274 best_col = i;
275 }
276 hit = 0;
277 /* Basic signal level test and the twist test */
278 if (row_energy[best_row] >= DTMF_THRESHOLD
279 &&
280 col_energy[best_col] >= DTMF_THRESHOLD
281 &&
282 col_energy[best_col] < row_energy[best_row]*s->reverse_twist
283 &&
284 col_energy[best_col]*s->normal_twist > row_energy[best_row])
285 {
286 /* Relative peak test ... */
287 for (i = 0; i < 4; i++)
288 {
289 if ((i != best_col && col_energy[i]*DTMF_RELATIVE_PEAK_COL > col_energy[best_col])
290 ||
291 (i != best_row && row_energy[i]*DTMF_RELATIVE_PEAK_ROW > row_energy[best_row]))
292 {
293 break;
294 }
295 }
296 /* ... and fraction of total energy test */
297 if (i >= 4
298 &&
299 (row_energy[best_row] + col_energy[best_col]) > DTMF_TO_TOTAL_ENERGY*s->energy)
300 {
301 hit = dtmf_positions[(best_row << 2) + best_col];
302 }
303 }
304 /* The logic in the next test should ensure the following for different successive hit patterns:
305 -----ABB = start of digit B.
306 ----B-BB = start of digit B
307 ----A-BB = start of digit B
308 BBBBBABB = still in digit B.
309 BBBBBB-- = end of digit B
310 BBBBBBC- = end of digit B
311 BBBBACBB = B ends, then B starts again.
312 BBBBBBCC = B ends, then C starts.
313 BBBBBCDD = B ends, then D starts.
314 This can work with:
315 - Back to back differing digits. Back-to-back digits should
316 not happen. The spec. says there should be a gap between digits.
317 However, many real phones do not impose a gap, and rolling across
318 the keypad can produce little or no gap.
319 - It tolerates nasty phones that give a very wobbly start to a digit.
320 - VoIP can give sample slips. The phase jumps that produces will cause
321 the block it is in to give no detection. This logic will ride over a
322 single missed block, and not falsely declare a second digit. If the
323 hiccup happens in the wrong place on a minimum length digit, however
324 we would still fail to detect that digit. Could anything be done to
325 deal with that? Packet loss is clearly a no-go zone.
326 Note this is only relevant to VoIP using A-law, u-law or similar.
327 Low bit rate codecs scramble DTMF too much for it to be recognised,
328 and often slip in units larger than a sample. */
329 if (hit != s->in_digit)
330 {
331 if (s->last_hit != s->in_digit)
332 {
333 /* We have two successive indications that something has changed. */
334 /* To declare digit on, the hits must agree. Otherwise we declare tone off. */
335 hit = (hit && hit == s->last_hit) ? hit : 0;
336 if (s->realtime_callback)
337 {
338 /* Avoid reporting multiple no digit conditions on flaky hits */
339 if (s->in_digit || hit)
340 s->realtime_callback(s->realtime_callback_data, hit);
341 }
342 else
343 {
344 if (hit)
345 {
346 if (s->current_digits < MAX_DTMF_DIGITS)
347 {
348 s->digits[s->current_digits++] = (char) hit;
349 s->digits[s->current_digits] = '\0';
350 if (s->callback)
351 {
352 s->callback(s->callback_data, s->digits, s->current_digits);
353 s->current_digits = 0;
354 }
355 }
356 else
357 {
358 s->lost_digits++;
359 }
360 }
361 }
362 s->in_digit = hit;
363 }
364 }
365 s->last_hit = hit;
366 /* Reinitialise the detector for the next block */
367 for (i = 0; i < 4; i++)
368 {
369 goertzel_reset(&s->row_out[i]);
370 goertzel_reset(&s->col_out[i]);
371 }
372 s->energy = 0.0;
373 s->current_sample = 0;
374 }
375 if (s->current_digits && s->callback)
376 {
377 s->callback(s->callback_data, s->digits, s->current_digits);
378 s->digits[0] = '\0';
379 s->current_digits = 0;
380 }
381 return 0;
382 }
383 /*- End of function --------------------------------------------------------*/
384
385 size_t dtmf_rx_get(dtmf_rx_state_t *s, char *buf, int max)
386 {
387 if (max > s->current_digits)
388 max = s->current_digits;
389 if (max > 0)
390 {
391 memcpy(buf, s->digits, max);
392 memmove(s->digits, s->digits + max, s->current_digits - max);
393 s->current_digits -= max;
394 }
395 buf[max] = '\0';
396 return max;
397 }
398 /*- End of function --------------------------------------------------------*/
399
400 void dtmf_rx_set_realtime_callback(dtmf_rx_state_t *s,
401 void (*callback)(void *user_data, int signal),
402 void *user_data)
403 {
404 s->realtime_callback = callback;
405 s->realtime_callback_data = user_data;
406 }
407 /*- End of function --------------------------------------------------------*/
408
409 void dtmf_rx_parms(dtmf_rx_state_t *s,
410 int filter_dialtone,
411 int twist,
412 int reverse_twist)
413 {
414 if (filter_dialtone >= 0)
415 {
416 s->z350_1 = 0.0;
417 s->z350_2 = 0.0;
418 s->z440_1 = 0.0;
419 s->z440_2 = 0.0;
420 s->filter_dialtone = filter_dialtone;
421 }
422 if (twist >= 0)
423 s->normal_twist = powf(10.0, twist/10.0);
424 if (reverse_twist >= 0)
425 s->reverse_twist = powf(10.0, reverse_twist/10.0);
426 }
427 /*- End of function --------------------------------------------------------*/
428
429 dtmf_rx_state_t *dtmf_rx_init(dtmf_rx_state_t *s,
430 void (*callback)(void *user_data, const char *digits, int len),
431 void *user_data)
432 {
433 int i;
434 static int initialised = FALSE;
435
436 s->callback = callback;
437 s->callback_data = user_data;
438 s->realtime_callback = NULL;
439 s->realtime_callback_data = NULL;
440 s->filter_dialtone = FALSE;
441 s->normal_twist = DTMF_NORMAL_TWIST;
442 s->reverse_twist = DTMF_REVERSE_TWIST;
443
444 s->in_digit = 0;
445 s->last_hit = 0;
446
447 if (!initialised)
448 {
449 for (i = 0; i < 4; i++)
450 {
451 make_goertzel_descriptor(&dtmf_detect_row[i], dtmf_row[i], 102);
452 make_goertzel_descriptor(&dtmf_detect_col[i], dtmf_col[i], 102);
453 }
454 initialised = TRUE;
455 }
456 for (i = 0; i < 4; i++)
457 {
458 goertzel_init(&s->row_out[i], &dtmf_detect_row[i]);
459 goertzel_init(&s->col_out[i], &dtmf_detect_col[i]);
460 }
461 s->energy = 0.0;
462 s->current_sample = 0;
463 s->lost_digits = 0;
464 s->current_digits = 0;
465 s->digits[0] = '\0';
466 return s;
467 }
468 /*- End of function --------------------------------------------------------*/
469
470 static void dtmf_tx_initialise(void)
471 {
472 int row;
473 int col;
474
475 if (dtmf_tx_inited)
476 return;
477 for (row = 0; row < 4; row++)
478 {
479 for (col = 0; col < 4; col++)
480 {
481 make_tone_gen_descriptor(&dtmf_digit_tones[row*4 + col],
482 (int) dtmf_row[row],
483 -10,
484 (int) dtmf_col[col],
485 -10,
486 50,
487 55,
488 0,
489 0,
490 FALSE);
491 }
492 }
493 dtmf_tx_inited = TRUE;
494 }
495 /*- End of function --------------------------------------------------------*/
496
497 int dtmf_tx(dtmf_tx_state_t *s, int16_t amp[], int max_samples)
498 {
499 int len;
500 size_t dig;
501 char *cp;
502
503 len = 0;
504 if (s->tones.current_section >= 0)
505 {
506 /* Deal with the fragment left over from last time */
507 len = tone_gen(&(s->tones), amp, max_samples);
508 }
509 dig = 0;
510 while (dig < s->current_digits && len < max_samples)
511 {
512 /* Step to the next digit */
513 if ((cp = strchr(dtmf_positions, s->digits[dig++])) == NULL)
514 continue;
515 tone_gen_init(&(s->tones), &(s->tone_descriptors[cp - dtmf_positions]));
516 len += tone_gen(&(s->tones), amp + len, max_samples - len);
517 }
518 if (dig)
519 {
520 /* Shift out the consumed digits */
521 s->current_digits -= dig;
522 memmove(s->digits, s->digits + dig, s->current_digits);
523 }
524 return len;
525 }
526 /*- End of function --------------------------------------------------------*/
527
528 size_t dtmf_tx_put(dtmf_tx_state_t *s, const char *digits)
529 {
530 size_t len;
531
532 /* This returns the number of characters that would not fit in the buffer.
533 The buffer will only be loaded if the whole string of digits will fit,
534 in which case zero is returned. */
535 if ((len = strlen(digits)) > 0)
536 {
537 if (s->current_digits + len <= MAX_DTMF_DIGITS)
538 {
539 memcpy(s->digits + s->current_digits, digits, len);
540 s->current_digits += len;
541 len = 0;
542 }
543 else
544 {
545 len = MAX_DTMF_DIGITS - s->current_digits;
546 }
547 }
548 return len;
549 }
550 /*- End of function --------------------------------------------------------*/
551
552 dtmf_tx_state_t *dtmf_tx_init(dtmf_tx_state_t *s)
553 {
554 if (!dtmf_tx_inited)
555 dtmf_tx_initialise();
556 s->tone_descriptors = dtmf_digit_tones;
557 tone_gen_init(&(s->tones), &dtmf_digit_tones[0]);
558 s->current_sample = 0;
559 s->current_digits = 0;
560 s->tones.current_section = -1;
561 return s;
562 }
563 /*- End of function --------------------------------------------------------*/
564 /*- End of file ------------------------------------------------------------*/

Repositories maintained by Peter Meerwald, pmeerw@pmeerw.net.