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