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