comparison spandsp-0.0.6pre17/src/dtmf.c @ 4:26cd8f1ef0b1

import spandsp-0.0.6pre17
author Peter Meerwald <pmeerw@cosy.sbg.ac.at>
date Fri, 25 Jun 2010 15:50:58 +0200
parents
children
comparison
equal deleted inserted replaced
3:c6c5a16ce2f2 4:26cd8f1ef0b1
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 Lesser General Public License version 2.1,
14 * as 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 Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public
22 * License 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.53 2009/04/12 09:12:10 steveu Exp $
26 */
27
28 /*! \file */
29
30 #if defined(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 "floating_fudge.h"
43 #include <string.h>
44 #include <stdio.h>
45 #include <time.h>
46 #include <fcntl.h>
47
48 #include "spandsp/telephony.h"
49 #include "spandsp/fast_convert.h"
50 #include "spandsp/queue.h"
51 #include "spandsp/complex.h"
52 #include "spandsp/dds.h"
53 #include "spandsp/tone_detect.h"
54 #include "spandsp/tone_generate.h"
55 #include "spandsp/super_tone_rx.h"
56 #include "spandsp/dtmf.h"
57
58 #include "spandsp/private/queue.h"
59 #include "spandsp/private/tone_generate.h"
60 #include "spandsp/private/dtmf.h"
61
62 #define DEFAULT_DTMF_TX_LEVEL -10
63 #define DEFAULT_DTMF_TX_ON_TIME 50
64 #define DEFAULT_DTMF_TX_OFF_TIME 55
65
66 #if defined(SPANDSP_USE_FIXED_POINT)
67 #define DTMF_THRESHOLD 10438 /* -42dBm0 */
68 #define DTMF_NORMAL_TWIST 6.309f /* 8dB */
69 #define DTMF_REVERSE_TWIST 2.512f /* 4dB */
70 #define DTMF_RELATIVE_PEAK_ROW 6.309f /* 8dB */
71 #define DTMF_RELATIVE_PEAK_COL 6.309f /* 8dB */
72 #define DTMF_TO_TOTAL_ENERGY 83.868f /* -0.85dB */
73 #define DTMF_POWER_OFFSET 68.251f /* 10*log(256.0*256.0*DTMF_SAMPLES_PER_BLOCK) */
74 #define DTMF_SAMPLES_PER_BLOCK 102
75 #else
76 #define DTMF_THRESHOLD 171032462.0f /* -42dBm0 [((DTMF_SAMPLES_PER_BLOCK*32768.0/1.4142)*10^((-42 - DBM0_MAX_SINE_POWER)/20.0))^2 => 171032462.0] */
77 #define DTMF_NORMAL_TWIST 6.309f /* 8dB [10^(8/10) => 6.309] */
78 #define DTMF_REVERSE_TWIST 2.512f /* 4dB */
79 #define DTMF_RELATIVE_PEAK_ROW 6.309f /* 8dB */
80 #define DTMF_RELATIVE_PEAK_COL 6.309f /* 8dB */
81 #define DTMF_TO_TOTAL_ENERGY 83.868f /* -0.85dB [DTMF_SAMPLES_PER_BLOCK*10^(-0.85/10.0)] */
82 #define DTMF_POWER_OFFSET 110.395f /* 10*log(32768.0*32768.0*DTMF_SAMPLES_PER_BLOCK) */
83 #define DTMF_SAMPLES_PER_BLOCK 102
84 #endif
85
86 static const float dtmf_row[] =
87 {
88 697.0f, 770.0f, 852.0f, 941.0f
89 };
90 static const float dtmf_col[] =
91 {
92 1209.0f, 1336.0f, 1477.0f, 1633.0f
93 };
94
95 static const char dtmf_positions[] = "123A" "456B" "789C" "*0#D";
96
97 static goertzel_descriptor_t dtmf_detect_row[4];
98 static goertzel_descriptor_t dtmf_detect_col[4];
99
100 static int dtmf_tx_inited = FALSE;
101 static tone_gen_descriptor_t dtmf_digit_tones[16];
102
103 SPAN_DECLARE(int) dtmf_rx(dtmf_rx_state_t *s, const int16_t amp[], int samples)
104 {
105 #if defined(SPANDSP_USE_FIXED_POINT)
106 int32_t row_energy[4];
107 int32_t col_energy[4];
108 int16_t xamp;
109 float famp;
110 #else
111 float row_energy[4];
112 float col_energy[4];
113 float xamp;
114 float famp;
115 #endif
116 float v1;
117 int i;
118 int j;
119 int sample;
120 int best_row;
121 int best_col;
122 int limit;
123 uint8_t hit;
124
125 hit = 0;
126 for (sample = 0; sample < samples; sample = limit)
127 {
128 /* The block length is optimised to meet the DTMF specs. */
129 if ((samples - sample) >= (DTMF_SAMPLES_PER_BLOCK - s->current_sample))
130 limit = sample + (DTMF_SAMPLES_PER_BLOCK - s->current_sample);
131 else
132 limit = samples;
133 /* The following unrolled loop takes only 35% (rough estimate) of the
134 time of a rolled loop on the machine on which it was developed */
135 for (j = sample; j < limit; j++)
136 {
137 xamp = amp[j];
138 if (s->filter_dialtone)
139 {
140 famp = xamp;
141 /* Sharp notches applied at 350Hz and 440Hz - the two common dialtone frequencies.
142 These are rather high Q, to achieve the required narrowness, without using lots of
143 sections. */
144 v1 = 0.98356f*famp + 1.8954426f*s->z350[0] - 0.9691396f*s->z350[1];
145 famp = v1 - 1.9251480f*s->z350[0] + s->z350[1];
146 s->z350[1] = s->z350[0];
147 s->z350[0] = v1;
148
149 v1 = 0.98456f*famp + 1.8529543f*s->z440[0] - 0.9691396f*s->z440[1];
150 famp = v1 - 1.8819938f*s->z440[0] + s->z440[1];
151 s->z440[1] = s->z440[0];
152 s->z440[0] = v1;
153 xamp = famp;
154 }
155 xamp = goertzel_preadjust_amp(xamp);
156 #if defined(SPANDSP_USE_FIXED_POINT)
157 s->energy += ((int32_t) xamp*xamp);
158 #else
159 s->energy += xamp*xamp;
160 #endif
161 goertzel_samplex(&s->row_out[0], xamp);
162 goertzel_samplex(&s->col_out[0], xamp);
163 goertzel_samplex(&s->row_out[1], xamp);
164 goertzel_samplex(&s->col_out[1], xamp);
165 goertzel_samplex(&s->row_out[2], xamp);
166 goertzel_samplex(&s->col_out[2], xamp);
167 goertzel_samplex(&s->row_out[3], xamp);
168 goertzel_samplex(&s->col_out[3], xamp);
169 }
170 s->current_sample += (limit - sample);
171 if (s->current_sample < DTMF_SAMPLES_PER_BLOCK)
172 continue;
173
174 /* We are at the end of a DTMF detection block */
175 /* Find the peak row and the peak column */
176 row_energy[0] = goertzel_result(&s->row_out[0]);
177 best_row = 0;
178 col_energy[0] = goertzel_result(&s->col_out[0]);
179 best_col = 0;
180 for (i = 1; i < 4; i++)
181 {
182 row_energy[i] = goertzel_result(&s->row_out[i]);
183 if (row_energy[i] > row_energy[best_row])
184 best_row = i;
185 col_energy[i] = goertzel_result(&s->col_out[i]);
186 if (col_energy[i] > col_energy[best_col])
187 best_col = i;
188 }
189 hit = 0;
190 /* Basic signal level test and the twist test */
191 if (row_energy[best_row] >= s->threshold
192 &&
193 col_energy[best_col] >= s->threshold
194 &&
195 col_energy[best_col] < row_energy[best_row]*s->reverse_twist
196 &&
197 col_energy[best_col]*s->normal_twist > row_energy[best_row])
198 {
199 /* Relative peak test ... */
200 for (i = 0; i < 4; i++)
201 {
202 if ((i != best_col && col_energy[i]*DTMF_RELATIVE_PEAK_COL > col_energy[best_col])
203 ||
204 (i != best_row && row_energy[i]*DTMF_RELATIVE_PEAK_ROW > row_energy[best_row]))
205 {
206 break;
207 }
208 }
209 /* ... and fraction of total energy test */
210 if (i >= 4
211 &&
212 (row_energy[best_row] + col_energy[best_col]) > DTMF_TO_TOTAL_ENERGY*s->energy)
213 {
214 /* Got a hit */
215 hit = dtmf_positions[(best_row << 2) + best_col];
216 }
217 }
218 /* The logic in the next test should ensure the following for different successive hit patterns:
219 -----ABB = start of digit B.
220 ----B-BB = start of digit B
221 ----A-BB = start of digit B
222 BBBBBABB = still in digit B.
223 BBBBBB-- = end of digit B
224 BBBBBBC- = end of digit B
225 BBBBACBB = B ends, then B starts again.
226 BBBBBBCC = B ends, then C starts.
227 BBBBBCDD = B ends, then D starts.
228 This can work with:
229 - Back to back differing digits. Back-to-back digits should
230 not happen. The spec. says there should be a gap between digits.
231 However, many real phones do not impose a gap, and rolling across
232 the keypad can produce little or no gap.
233 - It tolerates nasty phones that give a very wobbly start to a digit.
234 - VoIP can give sample slips. The phase jumps that produces will cause
235 the block it is in to give no detection. This logic will ride over a
236 single missed block, and not falsely declare a second digit. If the
237 hiccup happens in the wrong place on a minimum length digit, however
238 we would still fail to detect that digit. Could anything be done to
239 deal with that? Packet loss is clearly a no-go zone.
240 Note this is only relevant to VoIP using A-law, u-law or similar.
241 Low bit rate codecs scramble DTMF too much for it to be recognised,
242 and often slip in units larger than a sample. */
243 if (hit != s->in_digit)
244 {
245 if (s->last_hit != s->in_digit)
246 {
247 /* We have two successive indications that something has changed. */
248 /* To declare digit on, the hits must agree. Otherwise we declare tone off. */
249 hit = (hit && hit == s->last_hit) ? hit : 0;
250 if (s->realtime_callback)
251 {
252 /* Avoid reporting multiple no digit conditions on flaky hits */
253 if (s->in_digit || hit)
254 {
255 i = (s->in_digit && !hit) ? -99 : lfastrintf(log10f(s->energy)*10.0f - DTMF_POWER_OFFSET + DBM0_MAX_POWER);
256 s->realtime_callback(s->realtime_callback_data, hit, i, 0);
257 }
258 }
259 else
260 {
261 if (hit)
262 {
263 if (s->current_digits < MAX_DTMF_DIGITS)
264 {
265 s->digits[s->current_digits++] = (char) hit;
266 s->digits[s->current_digits] = '\0';
267 if (s->digits_callback)
268 {
269 s->digits_callback(s->digits_callback_data, s->digits, s->current_digits);
270 s->current_digits = 0;
271 }
272 }
273 else
274 {
275 s->lost_digits++;
276 }
277 }
278 }
279 s->in_digit = hit;
280 }
281 }
282 s->last_hit = hit;
283 #if defined(SPANDSP_USE_FIXED_POINT)
284 s->energy = 0;
285 #else
286 s->energy = 0.0f;
287 #endif
288 s->current_sample = 0;
289 }
290 if (s->current_digits && s->digits_callback)
291 {
292 s->digits_callback(s->digits_callback_data, s->digits, s->current_digits);
293 s->digits[0] = '\0';
294 s->current_digits = 0;
295 }
296 return 0;
297 }
298 /*- End of function --------------------------------------------------------*/
299
300 SPAN_DECLARE(int) dtmf_rx_status(dtmf_rx_state_t *s)
301 {
302 if (s->in_digit)
303 return s->in_digit;
304 if (s->last_hit)
305 return 'x';
306 return 0;
307 }
308 /*- End of function --------------------------------------------------------*/
309
310 SPAN_DECLARE(size_t) dtmf_rx_get(dtmf_rx_state_t *s, char *buf, int max)
311 {
312 if (max > s->current_digits)
313 max = s->current_digits;
314 if (max > 0)
315 {
316 memcpy(buf, s->digits, max);
317 memmove(s->digits, s->digits + max, s->current_digits - max);
318 s->current_digits -= max;
319 }
320 buf[max] = '\0';
321 return max;
322 }
323 /*- End of function --------------------------------------------------------*/
324
325 SPAN_DECLARE(void) dtmf_rx_set_realtime_callback(dtmf_rx_state_t *s,
326 tone_report_func_t callback,
327 void *user_data)
328 {
329 s->realtime_callback = callback;
330 s->realtime_callback_data = user_data;
331 }
332 /*- End of function --------------------------------------------------------*/
333
334 SPAN_DECLARE(void) dtmf_rx_parms(dtmf_rx_state_t *s,
335 int filter_dialtone,
336 int twist,
337 int reverse_twist,
338 int threshold)
339 {
340 float x;
341
342 if (filter_dialtone >= 0)
343 {
344 s->z350[0] = 0.0f;
345 s->z350[1] = 0.0f;
346 s->z440[0] = 0.0f;
347 s->z440[1] = 0.0f;
348 s->filter_dialtone = filter_dialtone;
349 }
350 if (twist >= 0)
351 s->normal_twist = powf(10.0f, twist/10.0f);
352 if (reverse_twist >= 0)
353 s->reverse_twist = powf(10.0f, reverse_twist/10.0f);
354 if (threshold > -99)
355 {
356 x = (DTMF_SAMPLES_PER_BLOCK*32768.0f/1.4142f)*powf(10.0f, (threshold - DBM0_MAX_SINE_POWER)/20.0f);
357 s->threshold = x*x;
358 }
359 }
360 /*- End of function --------------------------------------------------------*/
361
362 SPAN_DECLARE(dtmf_rx_state_t *) dtmf_rx_init(dtmf_rx_state_t *s,
363 digits_rx_callback_t callback,
364 void *user_data)
365 {
366 int i;
367 static int initialised = FALSE;
368
369 if (s == NULL)
370 {
371 if ((s = (dtmf_rx_state_t *) malloc(sizeof (*s))) == NULL)
372 return NULL;
373 }
374 s->digits_callback = callback;
375 s->digits_callback_data = user_data;
376 s->realtime_callback = NULL;
377 s->realtime_callback_data = NULL;
378 s->filter_dialtone = FALSE;
379 s->normal_twist = DTMF_NORMAL_TWIST;
380 s->reverse_twist = DTMF_REVERSE_TWIST;
381 s->threshold = DTMF_THRESHOLD;
382
383 s->in_digit = 0;
384 s->last_hit = 0;
385
386 if (!initialised)
387 {
388 for (i = 0; i < 4; i++)
389 {
390 make_goertzel_descriptor(&dtmf_detect_row[i], dtmf_row[i], DTMF_SAMPLES_PER_BLOCK);
391 make_goertzel_descriptor(&dtmf_detect_col[i], dtmf_col[i], DTMF_SAMPLES_PER_BLOCK);
392 }
393 initialised = TRUE;
394 }
395 for (i = 0; i < 4; i++)
396 {
397 goertzel_init(&s->row_out[i], &dtmf_detect_row[i]);
398 goertzel_init(&s->col_out[i], &dtmf_detect_col[i]);
399 }
400 #if defined(SPANDSP_USE_FIXED_POINT)
401 s->energy = 0;
402 #else
403 s->energy = 0.0f;
404 #endif
405 s->current_sample = 0;
406 s->lost_digits = 0;
407 s->current_digits = 0;
408 s->digits[0] = '\0';
409 return s;
410 }
411 /*- End of function --------------------------------------------------------*/
412
413 SPAN_DECLARE(int) dtmf_rx_release(dtmf_rx_state_t *s)
414 {
415 return 0;
416 }
417 /*- End of function --------------------------------------------------------*/
418
419 SPAN_DECLARE(int) dtmf_rx_free(dtmf_rx_state_t *s)
420 {
421 free(s);
422 return 0;
423 }
424 /*- End of function --------------------------------------------------------*/
425
426 static void dtmf_tx_initialise(void)
427 {
428 int row;
429 int col;
430
431 if (dtmf_tx_inited)
432 return;
433 for (row = 0; row < 4; row++)
434 {
435 for (col = 0; col < 4; col++)
436 {
437 make_tone_gen_descriptor(&dtmf_digit_tones[row*4 + col],
438 (int) dtmf_row[row],
439 DEFAULT_DTMF_TX_LEVEL,
440 (int) dtmf_col[col],
441 DEFAULT_DTMF_TX_LEVEL,
442 DEFAULT_DTMF_TX_ON_TIME,
443 DEFAULT_DTMF_TX_OFF_TIME,
444 0,
445 0,
446 FALSE);
447 }
448 }
449 dtmf_tx_inited = TRUE;
450 }
451 /*- End of function --------------------------------------------------------*/
452
453 SPAN_DECLARE(int) dtmf_tx(dtmf_tx_state_t *s, int16_t amp[], int max_samples)
454 {
455 int len;
456 const char *cp;
457 int digit;
458
459 len = 0;
460 if (s->tones.current_section >= 0)
461 {
462 /* Deal with the fragment left over from last time */
463 len = tone_gen(&(s->tones), amp, max_samples);
464 }
465 while (len < max_samples && (digit = queue_read_byte(&s->queue.queue)) >= 0)
466 {
467 /* Step to the next digit */
468 if (digit == 0)
469 continue;
470 if ((cp = strchr(dtmf_positions, digit)) == NULL)
471 continue;
472 tone_gen_init(&(s->tones), &dtmf_digit_tones[cp - dtmf_positions]);
473 s->tones.tone[0].gain = s->low_level;
474 s->tones.tone[1].gain = s->high_level;
475 s->tones.duration[0] = s->on_time;
476 s->tones.duration[1] = s->off_time;
477 len += tone_gen(&(s->tones), amp + len, max_samples - len);
478 }
479 return len;
480 }
481 /*- End of function --------------------------------------------------------*/
482
483 SPAN_DECLARE(int) dtmf_tx_put(dtmf_tx_state_t *s, const char *digits, int len)
484 {
485 size_t space;
486
487 /* This returns the number of characters that would not fit in the buffer.
488 The buffer will only be loaded if the whole string of digits will fit,
489 in which case zero is returned. */
490 if (len < 0)
491 {
492 if ((len = strlen(digits)) == 0)
493 return 0;
494 }
495 if ((space = queue_free_space(&s->queue.queue)) < (size_t) len)
496 return len - (int) space;
497 if (queue_write(&s->queue.queue, (const uint8_t *) digits, len) >= 0)
498 return 0;
499 return -1;
500 }
501 /*- End of function --------------------------------------------------------*/
502
503 SPAN_DECLARE(void) dtmf_tx_set_level(dtmf_tx_state_t *s, int level, int twist)
504 {
505 s->low_level = dds_scaling_dbm0f((float) level);
506 s->high_level = dds_scaling_dbm0f((float) (level + twist));
507 }
508 /*- End of function --------------------------------------------------------*/
509
510 SPAN_DECLARE(void) dtmf_tx_set_timing(dtmf_tx_state_t *s, int on_time, int off_time)
511 {
512 s->on_time = ((on_time >= 0) ? on_time : DEFAULT_DTMF_TX_ON_TIME)*SAMPLE_RATE/1000;
513 s->off_time = ((off_time >= 0) ? off_time : DEFAULT_DTMF_TX_OFF_TIME)*SAMPLE_RATE/1000;
514 }
515 /*- End of function --------------------------------------------------------*/
516
517 SPAN_DECLARE(dtmf_tx_state_t *) dtmf_tx_init(dtmf_tx_state_t *s)
518 {
519 if (s == NULL)
520 {
521 if ((s = (dtmf_tx_state_t *) malloc(sizeof (*s))) == NULL)
522 return NULL;
523 }
524 if (!dtmf_tx_inited)
525 dtmf_tx_initialise();
526 tone_gen_init(&(s->tones), &dtmf_digit_tones[0]);
527 dtmf_tx_set_level(s, DEFAULT_DTMF_TX_LEVEL, 0);
528 dtmf_tx_set_timing(s, -1, -1);
529 queue_init(&s->queue.queue, MAX_DTMF_DIGITS, QUEUE_READ_ATOMIC | QUEUE_WRITE_ATOMIC);
530 s->tones.current_section = -1;
531 return s;
532 }
533 /*- End of function --------------------------------------------------------*/
534
535 SPAN_DECLARE(int) dtmf_tx_release(dtmf_tx_state_t *s)
536 {
537 return 0;
538 }
539 /*- End of function --------------------------------------------------------*/
540
541 SPAN_DECLARE(int) dtmf_tx_free(dtmf_tx_state_t *s)
542 {
543 free(s);
544 return 0;
545 }
546 /*- End of function --------------------------------------------------------*/
547 /*- End of file ------------------------------------------------------------*/

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