comparison spandsp-0.0.6pre17/src/sig_tone.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 * sig_tone.c - Signalling tone processing for the 2280Hz, 2600Hz and similar
5 * signalling tone used in older protocols.
6 *
7 * Written by Steve Underwood <steveu@coppice.org>
8 *
9 * Copyright (C) 2004 Steve Underwood
10 *
11 * All rights reserved.
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU Lesser General Public License version 2.1,
15 * as published by the Free Software Foundation.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Lesser General Public License for more details.
21 *
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this program; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 *
26 * $Id: sig_tone.c,v 1.33 2009/09/04 14:38:46 steveu Exp $
27 */
28
29 /*! \file */
30
31 #if defined(HAVE_CONFIG_H)
32 #include "config.h"
33 #endif
34
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <inttypes.h>
38 #if defined(HAVE_TGMATH_H)
39 #include <tgmath.h>
40 #endif
41 #if defined(HAVE_MATH_H)
42 #include <math.h>
43 #endif
44 #include "floating_fudge.h"
45 #include <memory.h>
46 #include <string.h>
47 #include <limits.h>
48
49 #undef SPANDSP_USE_FIXED_POINT
50 #include "spandsp/telephony.h"
51 #include "spandsp/fast_convert.h"
52 #include "spandsp/dc_restore.h"
53 #include "spandsp/saturated.h"
54 #include "spandsp/vector_int.h"
55 #include "spandsp/complex.h"
56 #include "spandsp/dds.h"
57 #include "spandsp/super_tone_rx.h"
58 #include "spandsp/sig_tone.h"
59
60 #include "spandsp/private/sig_tone.h"
61
62 /*! PI */
63 #define PI 3.14159265358979323
64
65 /* The coefficients for the data notch filter. This filter is also the
66 guard filter for tone detection. */
67
68 sig_tone_descriptor_t sig_tones[4] =
69 {
70 {
71 /* 2280Hz (e.g. AC15, and many other European protocols) */
72 {2280, 0},
73 {{-10, -20}, {0, 0}}, /* -10+-1 dBmO and -20+-1 dBm0 */
74 ms_to_samples(400), /* 300ms to 550ms */
75
76 ms_to_samples(225),
77
78 ms_to_samples(225),
79 TRUE,
80
81 24,
82 64,
83
84 1,
85 {
86 {
87 #if defined(SPANDSP_USE_FIXED_POINT)
88 { 3600, 14397, 32767},
89 { 0, -9425, -28954},
90 { 0, 14196, 32767},
91 { 0, -17393, -28954},
92 12,
93 #else
94 {0.878906f, 0.439362f, 1.0f},
95 {0.0f, -0.287627f, -0.883605f},
96 {0.0f, 0.433228f, 1.0f},
97 {0.0f, -0.530792f, -0.883605f},
98 #endif
99 },
100 {
101 #if defined(SPANDSP_USE_FIXED_POINT)
102 { 0, 0, 0},
103 { 0, 0, 0},
104 { 0, 0, 0},
105 { 0, 0, 0},
106 0,
107 #else
108 {0.0f, 0.0f, 0.0f},
109 {0.0f, 0.0f, 0.0f},
110 {0.0f, 0.0f, 0.0f},
111 {0.0f, 0.0f, 0.0f},
112 #endif
113 }
114 },
115 #if defined(SPANDSP_USE_FIXED_POINT)
116 { 12900, -16384, -16384},
117 { 0, -8578, -11796},
118 15,
119 #else
120 {0.393676f, -0.5f, -0.5f},
121 {0.0f, -0.261778f, -0.359985f},
122 #endif
123
124 31744,
125 1024,
126
127 31744,
128 187,
129
130 31744,
131 187,
132
133 -1,
134 -32,
135
136 57
137 },
138 {
139 /* 2600Hz (e.g. many US protocols) */
140 {2600, 0},
141 {{-8, -8}, {0, 0}},
142 ms_to_samples(400),
143
144 ms_to_samples(225),
145
146 ms_to_samples(225),
147 FALSE,
148
149 24,
150 64,
151
152 1,
153 {
154 {
155 #if defined(SPANDSP_USE_FIXED_POINT)
156 { 3539, 29569, 32767},
157 { 0, -24010, -28341},
158 { 0, 29844, 32767},
159 { 0, -31208, -28341},
160 12,
161 #else
162 {0.864014f, 0.902374f, 1.0f},
163 {0.0f, -0.732727f, -0.864899f},
164 {0.0f, 0.910766f, 1.0f},
165 {0.0f, -0.952393f, -0.864899f},
166 #endif
167 },
168 {
169 #if defined(SPANDSP_USE_FIXED_POINT)
170 { 0, 0, 0},
171 { 0, 0, 0},
172 { 0, 0, 0},
173 { 0, 0, 0},
174 0,
175 #else
176 {0.0f, 0.0f, 0.0f},
177 {0.0f, 0.0f, 0.0f},
178 {0.0f, 0.0f, 0.0f},
179 {0.0f, 0.0f, 0.0f},
180 #endif
181 }
182 },
183 #if defined(SPANDSP_USE_FIXED_POINT)
184 { 32768, 0, 0},
185 { 0, 0, 0},
186 15,
187 #else
188 {1.0f, 0.0f, 0.0f},
189 {0.0f, 0.0f, 0.0f},
190 #endif
191
192 31744,
193 1024,
194
195 31744,
196 170,
197
198 31744,
199 170,
200
201 -1,
202 -32,
203
204 52
205 },
206 {
207 /* 2400Hz/2600Hz (e.g. SS5 and SS5bis) */
208 {2600, 2400},
209 {{-8, -8}, {-8, -8}},
210 ms_to_samples(400),
211
212 ms_to_samples(225),
213
214 ms_to_samples(225),
215 FALSE,
216
217 24,
218 64,
219
220 2,
221 {
222 {
223 #if defined(SPANDSP_USE_FIXED_POINT)
224 { 3539, 29569, 32767},
225 { 0, -24010, -28341},
226 { 0, 29844, 32767},
227 { 0, -31208, -28341},
228 12,
229 #else
230 {0.864014f, 0.902374f, 1.0f},
231 {0.0f, -0.732727f, -0.864899f},
232 {0.0f, 0.910766f, 1.0f},
233 {0.0f, -0.952393f, -0.864899f},
234 #endif
235 },
236 {
237 #if defined(SPANDSP_USE_FIXED_POINT)
238 { 3539, 20349, 32767},
239 { 0, -22075, -31856},
240 { 0, 20174, 32767},
241 { 0, -17832, -31836},
242 12,
243 #else
244 {0.864014f, 0.621007f, 1.0f},
245 {0.0f, -0.673667f, -0.972167f},
246 {0.0f, 0.615669f, 1.0f},
247 {0.0f, -0.544180f, -0.971546f},
248 #endif
249 }
250 },
251 #if defined(SPANDSP_USE_FIXED_POINT)
252 { 32768, 0, 0},
253 { 0, 0, 0},
254 15,
255 #else
256 {1.0f, 0.0f, 0.0f},
257 {0.0f, 0.0f, 0.0f},
258 #endif
259
260 31744,
261 1024,
262
263 31744,
264 170,
265
266 31744,
267 170,
268
269 -1,
270 -32,
271
272 52
273 }
274 };
275
276 SPAN_DECLARE(int) sig_tone_tx(sig_tone_tx_state_t *s, int16_t amp[], int len)
277 {
278 int i;
279 int j;
280 int n;
281 int16_t tone;
282 int need_update;
283 int high_low;
284
285 for (i = 0; i < len; i += n)
286 {
287 if (s->current_tx_timeout)
288 {
289 if (s->current_tx_timeout <= len - i)
290 {
291 n = s->current_tx_timeout;
292 need_update = TRUE;
293 }
294 else
295 {
296 n = len - i;
297 need_update = FALSE;
298 }
299 s->current_tx_timeout -= n;
300 }
301 else
302 {
303 n = len - i;
304 need_update = FALSE;
305 }
306 if (!(s->current_tx_tone & SIG_TONE_TX_PASSTHROUGH))
307 vec_zeroi16(&amp[i], n);
308 /*endif*/
309 if ((s->current_tx_tone & (SIG_TONE_1_PRESENT || SIG_TONE_2_PRESENT)))
310 {
311 /* Are we in the early phase (high tone energy level), or the sustaining
312 phase (low tone energy level) of tone generation? */
313 /* This doesn't try to get the high/low timing precise, as there is no
314 value in doing so. It works block by block, and the blocks are normally
315 quite short. */
316 if (s->high_low_timer > 0)
317 {
318 if (n > s->high_low_timer)
319 n = s->high_low_timer;
320 s->high_low_timer -= n;
321 high_low = 0;
322 }
323 else
324 {
325 high_low = 1;
326 }
327 /*endif*/
328 if ((s->current_tx_tone & SIG_TONE_1_PRESENT) && s->phase_rate[0])
329 {
330 for (j = i; j < i + n; j++)
331 {
332 tone = dds_mod(&(s->phase_acc[0]), s->phase_rate[0], s->tone_scaling[0][high_low], 0);
333 amp[j] = saturate(amp[j] + tone);
334 }
335 /*endfor*/
336 }
337 /*endif*/
338 if ((s->current_tx_tone & SIG_TONE_2_PRESENT) && s->phase_rate[1])
339 {
340 for (j = i; j < i + n; j++)
341 {
342 tone = dds_mod(&(s->phase_acc[1]), s->phase_rate[1], s->tone_scaling[1][high_low], 0);
343 amp[j] = saturate(amp[j] + tone);
344 }
345 /*endfor*/
346 }
347 /*endif*/
348 }
349 /*endif*/
350 if (need_update && s->sig_update)
351 s->sig_update(s->user_data, SIG_TONE_TX_UPDATE_REQUEST, 0, 0);
352 /*endif*/
353 }
354 /*endfor*/
355 return len;
356 }
357 /*- End of function --------------------------------------------------------*/
358
359 SPAN_DECLARE(void) sig_tone_tx_set_mode(sig_tone_tx_state_t *s, int mode, int duration)
360 {
361 int old_tones;
362 int new_tones;
363
364 old_tones = s->current_tx_tone & (SIG_TONE_1_PRESENT | SIG_TONE_2_PRESENT);
365 new_tones = mode & (SIG_TONE_1_PRESENT | SIG_TONE_2_PRESENT);
366 if (new_tones && old_tones != new_tones)
367 s->high_low_timer = s->desc->high_low_timeout;
368 /*endif*/
369 s->current_tx_tone = mode;
370 s->current_tx_timeout = duration;
371 }
372 /*- End of function --------------------------------------------------------*/
373
374 SPAN_DECLARE(sig_tone_tx_state_t *) sig_tone_tx_init(sig_tone_tx_state_t *s, int tone_type, tone_report_func_t sig_update, void *user_data)
375 {
376 int i;
377
378 if (sig_update == NULL || tone_type < 1 || tone_type > 3)
379 return NULL;
380 /*endif*/
381
382 if (s == NULL)
383 {
384 if ((s = (sig_tone_tx_state_t *) malloc(sizeof(*s))) == NULL)
385 return NULL;
386 }
387 memset(s, 0, sizeof(*s));
388
389 s->sig_update = sig_update;
390 s->user_data = user_data;
391
392 s->desc = &sig_tones[tone_type - 1];
393
394 for (i = 0; i < 2; i++)
395 {
396 if (s->desc->tone_freq[i])
397 s->phase_rate[i] = dds_phase_rate((float) s->desc->tone_freq[i]);
398 else
399 s->phase_rate[i] = 0;
400 s->tone_scaling[i][0] = dds_scaling_dbm0((float) s->desc->tone_amp[i][0]);
401 s->tone_scaling[i][1] = dds_scaling_dbm0((float) s->desc->tone_amp[i][1]);
402 }
403 return s;
404 }
405 /*- End of function --------------------------------------------------------*/
406
407 SPAN_DECLARE(int) sig_tone_tx_release(sig_tone_tx_state_t *s)
408 {
409 return 0;
410 }
411 /*- End of function --------------------------------------------------------*/
412
413 SPAN_DECLARE(int) sig_tone_tx_free(sig_tone_tx_state_t *s)
414 {
415 if (s)
416 free(s);
417 return 0;
418 }
419 /*- End of function --------------------------------------------------------*/
420
421 SPAN_DECLARE(int) sig_tone_rx(sig_tone_rx_state_t *s, int16_t amp[], int len)
422 {
423 #if defined(SPANDSP_USE_FIXED_POINT)
424 int32_t x;
425 int32_t notched_signal;
426 int32_t bandpass_signal;
427 #else
428 float x;
429 float notched_signal;
430 float bandpass_signal;
431 #endif
432 int i;
433 int j;
434 int32_t mown_notch[2];
435 int32_t mown_bandpass;
436
437 for (i = 0; i < len; i++)
438 {
439 if (s->signaling_state_duration < INT_MAX)
440 s->signaling_state_duration++;
441 /*endif*/
442 notched_signal = 0;
443 for (j = 0; j < s->desc->tones; j++)
444 {
445 /* The notch filter is two cascaded biquads. */
446 notched_signal = amp[i];
447
448 #if defined(SPANDSP_USE_FIXED_POINT)
449 notched_signal *= s->desc->tone[j].notch_a1[0];
450 notched_signal += s->tone[j].notch_z1[1]*s->desc->tone[j].notch_b1[1];
451 notched_signal += s->tone[j].notch_z1[2]*s->desc->tone[j].notch_b1[2];
452 x = notched_signal;
453 notched_signal += s->tone[j].notch_z1[1]*s->desc->tone[j].notch_a1[1];
454 notched_signal += s->tone[j].notch_z1[2]*s->desc->tone[j].notch_a1[2];
455 s->tone[j].notch_z1[2] = s->tone[j].notch_z1[1];
456 s->tone[j].notch_z1[1] = x >> 15;
457
458 notched_signal += s->tone[j].notch_z2[1]*s->desc->tone[j].notch_b2[1];
459 notched_signal += s->tone[j].notch_z2[2]*s->desc->tone[j].notch_b2[2];
460 x = notched_signal;
461 notched_signal += s->tone[j].notch_z2[1]*s->desc->tone[j].notch_a2[1];
462 notched_signal += s->tone[j].notch_z2[2]*s->desc->tone[j].notch_a2[2];
463 s->tone[j].notch_z2[2] = s->tone[j].notch_z2[1];
464 s->tone[j].notch_z2[1] = x >> 15;
465
466 notched_signal >>= s->desc->notch_postscale;
467 #else
468 notched_signal *= s->desc->tone[j].notch_a1[0];
469 notched_signal += s->tone[j].notch_z1[1]*s->desc->tone[j].notch_b1[1];
470 notched_signal += s->tone[j].notch_z1[2]*s->desc->tone[j].notch_b1[2];
471 x = notched_signal;
472 notched_signal += s->tone[j].notch_z1[1]*s->desc->tone[j].notch_a1[1];
473 notched_signal += s->tone[j].notch_z1[2]*s->desc->tone[j].notch_a1[2];
474 s->tone[j].notch_z1[2] = s->tone[j].notch_z1[1];
475 s->tone[j].notch_z1[1] = x;
476
477 notched_signal += s->tone[j].notch_z2[1]*s->desc->tone[j].notch_b2[1];
478 notched_signal += s->tone[j].notch_z2[2]*s->desc->tone[j].notch_b2[2];
479 x = notched_signal;
480 notched_signal += s->tone[j].notch_z2[1]*s->desc->tone[j].notch_a2[1];
481 notched_signal += s->tone[j].notch_z2[2]*s->desc->tone[j].notch_a2[2];
482 s->tone[j].notch_z2[2] = s->tone[j].notch_z2[1];
483 s->tone[j].notch_z2[1] = x;
484 #endif
485 /* Modulus and leaky integrate the notched data. The result of
486 this isn't used in low tone detect mode, but we must keep notch_zl
487 rolling along. */
488 s->tone[j].notch_zl = ((s->tone[j].notch_zl*s->desc->notch_slugi) >> 15)
489 + ((abs((int) notched_signal)*s->desc->notch_slugp) >> 15);
490 /* Mow the grass to weed out the noise! */
491 mown_notch[j] = s->tone[0].notch_zl & s->desc->notch_threshold;
492 }
493
494 if (s->tone_present)
495 {
496 if (s->flat_mode_timeout <= 0)
497 s->flat_mode = TRUE;
498 else
499 s->flat_mode_timeout--;
500 /*endif*/
501 }
502 else
503 {
504 s->flat_mode_timeout = s->desc->sharp_flat_timeout;
505 s->flat_mode = FALSE;
506 }
507 /*endif*/
508
509 if (s->flat_mode)
510 {
511 /* Flat mode */
512
513 /* The bandpass filter is a single bi-quad stage */
514 bandpass_signal = amp[i];
515 #if defined(SPANDSP_USE_FIXED_POINT)
516 bandpass_signal *= s->desc->broad_a[0];
517 bandpass_signal += s->broad_z[1]*s->desc->broad_b[1];
518 bandpass_signal += s->broad_z[2]*s->desc->broad_b[2];
519 x = bandpass_signal;
520 bandpass_signal += s->broad_z[1]*s->desc->broad_a[1];
521 bandpass_signal += s->broad_z[2]*s->desc->broad_a[2];
522 s->broad_z[2] = s->broad_z[1];
523 s->broad_z[1] = x >> 15;
524 bandpass_signal >>= s->desc->broad_postscale;
525 #else
526 bandpass_signal *= s->desc->broad_a[0];
527 bandpass_signal += s->broad_z[1]*s->desc->broad_b[1];
528 bandpass_signal += s->broad_z[2]*s->desc->broad_b[2];
529 x = bandpass_signal;
530 bandpass_signal += s->broad_z[1]*s->desc->broad_a[1];
531 bandpass_signal += s->broad_z[2]*s->desc->broad_a[2];
532 s->broad_z[2] = s->broad_z[1];
533 s->broad_z[1] = x;
534 #endif
535 /* Leaky integrate the bandpassed data */
536 s->broad_zl = ((s->broad_zl*s->desc->broad_slugi) >> 15)
537 + ((abs((int) bandpass_signal)*s->desc->broad_slugp) >> 15);
538
539 /* For the broad band receiver we use a simple linear threshold! */
540 if (s->tone_present)
541 {
542 s->tone_present = (s->broad_zl > s->desc->broad_threshold);
543 if (!s->tone_present)
544 {
545 if (s->sig_update)
546 s->sig_update(s->user_data, SIG_TONE_1_CHANGE, 0, s->signaling_state_duration);
547 /*endif*/
548 s->signaling_state_duration = 0;
549 }
550 /*endif*/
551 }
552 else
553 {
554 s->tone_present = (s->broad_zl > s->desc->broad_threshold);
555 if (s->tone_present)
556 {
557 if (s->sig_update)
558 s->sig_update(s->user_data, SIG_TONE_1_CHANGE | SIG_TONE_1_PRESENT, 0, s->signaling_state_duration);
559 /*endif*/
560 s->signaling_state_duration = 0;
561 }
562 /*endif*/
563 }
564 /*endif*/
565
566 /* Notch insertion logic */
567 /* tone_present and tone_on are equivalent in flat mode */
568 if (s->tone_present)
569 {
570 s->notch_enabled = s->desc->notch_allowed;
571 s->notch_insertion_timeout = s->desc->notch_lag_time;
572 }
573 else
574 {
575 if (s->notch_insertion_timeout > 0)
576 s->notch_insertion_timeout--;
577 else
578 s->notch_enabled = FALSE;
579 /*endif*/
580 }
581 /*endif*/
582 }
583 else
584 {
585 /* Sharp mode */
586
587 /* Modulus and leaky integrate the data */
588 s->broad_zl = ((s->broad_zl*s->desc->unfiltered_slugi) >> 15)
589 + ((abs((int) amp[i])*s->desc->unfiltered_slugp) >> 15);
590
591 /* Mow the grass to weed out the noise! */
592 mown_bandpass = s->broad_zl & s->desc->unfiltered_threshold;
593
594 /* Persistence checking and notch insertion logic */
595 if (!s->tone_present)
596 {
597 if (mown_notch[0] < mown_bandpass)
598 {
599 /* Tone is detected this sample */
600 if (s->tone_persistence_timeout <= 0)
601 {
602 s->tone_present = TRUE;
603 s->notch_enabled = s->desc->notch_allowed;
604 s->tone_persistence_timeout = s->desc->tone_off_check_time;
605 s->notch_insertion_timeout = s->desc->notch_lag_time;
606 if (s->sig_update)
607 s->sig_update(s->user_data, SIG_TONE_1_CHANGE | SIG_TONE_1_PRESENT, 0, s->signaling_state_duration);
608 /*endif*/
609 s->signaling_state_duration = 0;
610 }
611 else
612 {
613 s->tone_persistence_timeout--;
614 if (s->notch_insertion_timeout > 0)
615 s->notch_insertion_timeout--;
616 else
617 s->notch_enabled = FALSE;
618 /*endif*/
619 }
620 /*endif*/
621 }
622 else
623 {
624 s->tone_persistence_timeout = s->desc->tone_on_check_time;
625 if (s->notch_insertion_timeout > 0)
626 s->notch_insertion_timeout--;
627 else
628 s->notch_enabled = FALSE;
629 /*endif*/
630 }
631 /*endif*/
632 }
633 else
634 {
635 if (mown_notch[0] > mown_bandpass)
636 {
637 /* Tone is not detected this sample */
638 if (s->tone_persistence_timeout <= 0)
639 {
640 s->tone_present = FALSE;
641 s->tone_persistence_timeout = s->desc->tone_on_check_time;
642 if (s->sig_update)
643 s->sig_update(s->user_data, SIG_TONE_1_CHANGE, 0, s->signaling_state_duration);
644 /*endif*/
645 s->signaling_state_duration = 0;
646 }
647 else
648 {
649 s->tone_persistence_timeout--;
650 }
651 /*endif*/
652 }
653 else
654 {
655 s->tone_persistence_timeout = s->desc->tone_off_check_time;
656 }
657 /*endif*/
658 }
659 /*endif*/
660 }
661 /*endif*/
662
663 if ((s->current_rx_tone & SIG_TONE_RX_PASSTHROUGH))
664 {
665 if ((s->current_rx_tone & SIG_TONE_RX_FILTER_TONE) || s->notch_enabled)
666 amp[i] = (int16_t) notched_signal;
667 /*endif*/
668 }
669 else
670 {
671 amp[i] = 0;
672 }
673 /*endif*/
674 }
675 /*endfor*/
676 return len;
677 }
678 /*- End of function --------------------------------------------------------*/
679
680 SPAN_DECLARE(void) sig_tone_rx_set_mode(sig_tone_rx_state_t *s, int mode, int duration)
681 {
682 s->current_rx_tone = mode;
683 }
684 /*- End of function --------------------------------------------------------*/
685
686 SPAN_DECLARE(sig_tone_rx_state_t *) sig_tone_rx_init(sig_tone_rx_state_t *s, int tone_type, tone_report_func_t sig_update, void *user_data)
687 {
688 if (sig_update == NULL || tone_type < 1 || tone_type > 3)
689 return NULL;
690 /*endif*/
691
692 if (s == NULL)
693 {
694 if ((s = (sig_tone_rx_state_t *) malloc(sizeof(*s))) == NULL)
695 return NULL;
696 }
697 memset(s, 0, sizeof(*s));
698
699 s->sig_update = sig_update;
700 s->user_data = user_data;
701
702 s->desc = &sig_tones[tone_type - 1];
703
704 s->flat_mode_timeout = 0;
705 s->notch_insertion_timeout = 0;
706 s->tone_persistence_timeout = 0;
707 s->signaling_state_duration = 0;
708 return s;
709 }
710 /*- End of function --------------------------------------------------------*/
711
712 SPAN_DECLARE(int) sig_tone_rx_release(sig_tone_rx_state_t *s)
713 {
714 return 0;
715 }
716 /*- End of function --------------------------------------------------------*/
717
718 SPAN_DECLARE(int) sig_tone_rx_free(sig_tone_rx_state_t *s)
719 {
720 if (s)
721 free(s);
722 return 0;
723 }
724 /*- End of function --------------------------------------------------------*/
725 /*- End of file ------------------------------------------------------------*/

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