Mercurial > hg > audiostuff
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 ------------------------------------------------------------*/ |