1 /*
2 * libsupertone - A library to handle all the world's telephony supervisory tones.
3 *
4 * supertone.c
5 *
6 * Written by Steve Underwood <steveu@coppice.org>
7 *
8 * Copyright (C) 2004 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: supertone.c,v 1.11 2008/07/04 14:19:09 steveu Exp $
26 */
27
28 /*! \file */
29
30 #if defined(HAVE_CONFIG_H)
31 #include <config.h>
32 #endif
33
34 #include <stdlib.h>
35 #include <limits.h>
36 #include <stdio.h>
37 #include <fcntl.h>
38 #include <stdarg.h>
39 #include <math.h>
40 #include <inttypes.h>
41 #include <unistd.h>
42 #include <string.h>
43 #include <strings.h>
44 #include <errno.h>
45 #include <signal.h>
46 #include <time.h>
47 #include <unistd.h>
48 #include <sys/time.h>
49 #include <sys/ioctl.h>
50
51 #include <libxml/xmlmemory.h>
52 #include <libxml/parser.h>
53 #include <libxml/xinclude.h>
54
55 #include <spandsp.h>
56 #include <spandsp/expose.h>
57
58 #include "supertone.h"
59 #include "libsupertone.h"
60
61 #define MAX_TONE_SETS 20
62
63 super_tone_set_t sets[MAX_TONE_SETS];
64
parse_tone(super_tone_rx_descriptor_t * desc,int tone_id,super_tone_tx_step_t ** tree,xmlDocPtr doc,xmlNsPtr ns,xmlNodePtr cur)65 static int parse_tone(super_tone_rx_descriptor_t *desc, int tone_id, super_tone_tx_step_t **tree, xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur)
66 {
67 xmlChar *x;
68 float f1;
69 float f2;
70 float f_tol;
71 float l1;
72 float l2;
73 float length;
74 float length_tol;
75 float recognition_length;
76 float recognition_length_tol;
77 int cycles;
78 int min_duration;
79 int max_duration;
80 super_tone_tx_step_t *treep;
81
82 cur = cur->xmlChildrenNode;
83 while (cur)
84 {
85 if (xmlStrcmp(cur->name, (const xmlChar *) "step") == 0)
86 {
87 printf("Step - ");
88 /* Set some defaults */
89 f1 = 0.0;
90 f2 = 0.0;
91 f_tol = 1.0;
92 l1 = -11.0;
93 l2 = -11.0;
94 length = 0.0;
95 length_tol = 10.0;
96 recognition_length = 0.0;
97 recognition_length_tol = 10.0;
98 cycles = 1;
99 if ((x = xmlGetProp(cur, (const xmlChar *) "freq")))
100 {
101 sscanf((const char *) x, "%f [%f%%]", &f1, &f_tol);
102 sscanf((const char *) x, "%f+%f [%f%%]", &f1, &f2, &f_tol);
103 printf("Frequency=%.2f+%.2f [%.2f%%] ", f1, f2, f_tol);
104 }
105 if ((x = xmlGetProp(cur, (const xmlChar *) "level")))
106 {
107 if (sscanf((const char *) x, "%f+%f", &l1, &l2) < 2)
108 l2 = l1;
109 printf("Level=%.2f+%.2f ", l1, l2);
110 }
111 if ((x = xmlGetProp(cur, (const xmlChar *) "length")))
112 {
113 sscanf((const char *) x, "%f [%f%%]", &length, &length_tol);
114 printf("Length=%.2f [%.2f%%] ", length, length_tol);
115 }
116 if ((x = xmlGetProp(cur, (const xmlChar *) "recognition-length")))
117 {
118 sscanf((const char *) x, "%f [%f%%]", &recognition_length, &recognition_length_tol);
119 printf("Recognition length=%.2f [%.2f%%] ", recognition_length, recognition_length_tol);
120 }
121 if ((x = xmlGetProp(cur, (const xmlChar *) "cycles")))
122 {
123 if (xmlStrcasecmp(x, (const xmlChar *) "endless") == 0)
124 cycles = 0;
125 else
126 cycles = atoi((const char *) x);
127 printf("Cycles=%d ", cycles);
128 }
129 if ((x = xmlGetProp(cur, (const xmlChar *) "recorded-announcement")))
130 printf("Recorded announcement='%s' ", x);
131 printf("\n");
132 if (f1 || f2 || length)
133 {
134 /* TODO: This cannot handle cycling patterns */
135 if (length == 0.0)
136 {
137 if (recognition_length)
138 min_duration = recognition_length*1000.0 + 0.5;
139 else
140 min_duration = 700;
141 max_duration = 0;
142 }
143 else
144 {
145 if (recognition_length)
146 min_duration = recognition_length*1000.0 + 0.5;
147 else
148 min_duration = (length*1000.0 + 0.5)*(1.0 - length_tol/100.0) - 30;
149 max_duration = (length*1000.0 + 0.5)*(1.0 + length_tol/100.0) + 30;
150 }
151 printf(">>>Detector element %10d %10d %10d %10d\n", (int) (f1 + 0.5), (int) (f2 + 0.5), min_duration, max_duration);
152 super_tone_rx_add_element(desc, tone_id, f1 + 0.5, f2 + 0.5, min_duration, max_duration);
153 }
154 treep = super_tone_tx_make_step(NULL,
155 f1,
156 l1,
157 f2,
158 l2,
159 length*1000.0 + 0.5,
160 cycles);
161 *tree = treep;
162 tree = &(treep->next);
163 parse_tone(desc, tone_id, &(treep->nest), doc, ns, cur);
164 }
165 /*endif*/
166 cur = cur->next;
167 }
168 /*endwhile*/
169 return 0;
170 }
171 /*- End of function --------------------------------------------------------*/
172
parse_tone_set(super_tone_set_t * s,xmlDocPtr doc,xmlNsPtr ns,xmlNodePtr cur)173 static void parse_tone_set(super_tone_set_t *s, xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur)
174 {
175 int tone_id;
176 xmlChar *x;
177
178 printf("Parsing tone set\n");
179 cur = cur->xmlChildrenNode;
180 while (cur)
181 {
182 if (xmlStrcmp(cur->name, (const xmlChar *) "dial-tone") == 0)
183 {
184 printf("Hit %s\n", cur->name);
185 tone_id = super_tone_rx_add_tone(s->tone_detector);
186 s->tone[ST_TYPE_DIALTONE] = NULL;
187 parse_tone(s->tone_detector, tone_id, &s->tone[ST_TYPE_DIALTONE], doc, ns, cur);
188 s->tone_names[tone_id] = (const char *) xmlStrdup(cur->name);
189 if ((x = xmlGetProp(cur, (const xmlChar *) "domain")))
190 s->tone_domains[tone_id] = (const char *) xmlStrdup(x);
191 /*endif*/
192 s->tone_types[tone_id] = ST_TYPE_DIALTONE;
193 }
194 else if (xmlStrcmp(cur->name, (const xmlChar *) "ringback-tone") == 0)
195 {
196 printf("Hit %s\n", cur->name);
197 tone_id = super_tone_rx_add_tone(s->tone_detector);
198 s->tone[ST_TYPE_RINGBACK] = NULL;
199 parse_tone(s->tone_detector, tone_id, &s->tone[ST_TYPE_RINGBACK], doc, ns, cur);
200 s->tone_names[tone_id] = (const char *) xmlStrdup(cur->name);
201 if ((x = xmlGetProp(cur, (const xmlChar *) "domain")))
202 s->tone_domains[tone_id] = (const char *) xmlStrdup(x);
203 /*endif*/
204 s->tone_types[tone_id] = ST_TYPE_RINGBACK;
205 }
206 else if (xmlStrcmp(cur->name, (const xmlChar *) "busy-tone") == 0)
207 {
208 printf("Hit %s\n", cur->name);
209 tone_id = super_tone_rx_add_tone(s->tone_detector);
210 s->tone[ST_TYPE_BUSY] = NULL;
211 parse_tone(s->tone_detector, tone_id, &s->tone[ST_TYPE_BUSY], doc, ns, cur);
212 s->tone_names[tone_id] = strdup((const char *) cur->name);
213 if ((x = xmlGetProp(cur, (const xmlChar *) "domain")))
214 s->tone_domains[tone_id] = (const char *) xmlStrdup(x);
215 /*endif*/
216 s->tone_types[tone_id] = ST_TYPE_BUSY;
217 /* Usually the disconnect tone is the same as busy tone,
218 so use busy tone if we have not found a specifc
219 disconnect tone. */
220 if (s->tone[ST_TYPE_DISCONNECTED] == NULL)
221 s->tone[ST_TYPE_DISCONNECTED] = s->tone[ST_TYPE_BUSY];
222 /*endif*/
223 }
224 else if (xmlStrcmp(cur->name, (const xmlChar *) "number-unobtainable-tone") == 0)
225 {
226 printf("Hit %s\n", cur->name);
227 tone_id = super_tone_rx_add_tone(s->tone_detector);
228 s->tone[ST_TYPE_NU] = NULL;
229 parse_tone(s->tone_detector, tone_id, &s->tone[ST_TYPE_NU], doc, ns, cur);
230 s->tone_names[tone_id] = strdup((const char *) cur->name);
231 if ((x = xmlGetProp(cur, (const xmlChar *) "domain")))
232 s->tone_domains[tone_id] = strdup((const char *) x);
233 /*endif*/
234 s->tone_types[tone_id] = ST_TYPE_NU;
235 }
236 else if (xmlStrcmp(cur->name, (const xmlChar *) "congestion-tone") == 0)
237 {
238 printf("Hit %s\n", cur->name);
239 tone_id = super_tone_rx_add_tone(s->tone_detector);
240 s->tone[ST_TYPE_CONGESTED] = NULL;
241 parse_tone(s->tone_detector, tone_id, &s->tone[ST_TYPE_CONGESTED], doc, ns, cur);
242 s->tone_names[tone_id] = (const char *) xmlStrdup(cur->name);
243 if ((x = xmlGetProp(cur, (const xmlChar *) "domain")))
244 s->tone_domains[tone_id] = strdup((const char *) x);
245 /*endif*/
246 s->tone_types[tone_id] = ST_TYPE_CONGESTED;
247 }
248 else if (xmlStrcmp(cur->name, (const xmlChar *) "disconnect-tone") == 0)
249 {
250 printf("Hit %s\n", cur->name);
251 tone_id = super_tone_rx_add_tone(s->tone_detector);
252 s->tone[ST_TYPE_DISCONNECTED] = NULL;
253 parse_tone(s->tone_detector, tone_id, &s->tone[ST_TYPE_DISCONNECTED], doc, ns, cur);
254 s->tone_names[tone_id] = strdup((const char *) cur->name);
255 if ((x = xmlGetProp(cur, (const xmlChar *) "domain")))
256 s->tone_domains[tone_id] = (const char *) xmlStrdup(x);
257 /*endif*/
258 s->tone_types[tone_id] = ST_TYPE_DISCONNECTED;
259 }
260 /*endif*/
261 cur = cur->next;
262 }
263 /*endwhile*/
264 }
265 /*- End of function --------------------------------------------------------*/
266
get_supervisory_tone_set(const char * set_id)267 super_tone_set_t *get_supervisory_tone_set(const char *set_id)
268 {
269 xmlDocPtr doc;
270 xmlNsPtr ns;
271 xmlNodePtr cur;
272 #if 0
273 xmlValidCtxt valid;
274 #endif
275 xmlChar *x;
276 super_tone_set_t *s;
277 const char *tone_file;
278 int slot;
279
280 ns = NULL;
281 /* See if we have this set loaded already */
282 for (slot = 0; slot < MAX_TONE_SETS; slot++)
283 {
284 if (sets[slot].uncode && strcmp(sets[slot].uncode, set_id) == 0)
285 return &sets[slot];
286 /*endif*/
287 }
288 /*endfor*/
289
290 /* Find a free slot to use */
291 for (slot = 0; slot < MAX_TONE_SETS; slot++)
292 {
293 if (sets[slot].uncode == NULL)
294 break;
295 /*endif*/
296 }
297 /*endfor*/
298
299 if (slot >= MAX_TONE_SETS)
300 return NULL;
301 /*endif*/
302
303 s = &sets[slot];
304
305 tone_file = DATADIR "global-tones.xml";
306 if ((s->tone_detector = super_tone_rx_make_descriptor(NULL)) == NULL)
307 return NULL;
308 /*endif*/
309
310 xmlKeepBlanksDefault(0);
311 xmlCleanupParser();
312 doc = xmlParseFile(tone_file);
313 if (doc == NULL)
314 {
315 fprintf(stderr, "No document\n");
316 return NULL;
317 }
318 /*endif*/
319 xmlXIncludeProcess(doc);
320 #if 0
321 if (!xmlValidateDocument(&valid, doc))
322 {
323 fprintf(stderr, "Invalid document\n");
324 return NULL;
325 }
326 /*endif*/
327 #endif
328 /* Check the document is of the right kind */
329 if ((cur = xmlDocGetRootElement(doc)) == NULL)
330 {
331 fprintf(stderr, "Empty document\n");
332 xmlFreeDoc(doc);
333 return NULL;
334 }
335 /*endif*/
336 if (xmlStrcmp(cur->name, (const xmlChar *) "global-tones"))
337 {
338 fprintf(stderr, "Wrong type of document. Root node is not global-tones");
339 xmlFreeDoc(doc);
340 return NULL;
341 }
342 /*endif*/
343 cur = cur->xmlChildrenNode;
344 while (cur && xmlIsBlankNode(cur))
345 cur = cur->next;
346 /*endwhile*/
347 if (cur == NULL)
348 return NULL;
349 /*endif*/
350 while (cur)
351 {
352 if (xmlStrcmp(cur->name, (const xmlChar *) "tone-set") == 0)
353 {
354 if ((x = xmlGetProp(cur, (const xmlChar *) "uncode")))
355 {
356 if (xmlStrcmp(x, (const xmlChar *) set_id) == 0)
357 {
358 sets[slot].uncode = (const char *) xmlStrdup(x);
359 if ((x = xmlGetProp(cur, (const xmlChar *) "country")))
360 sets[slot].country = (const char *) xmlStrdup(x);
361 /*endif*/
362 parse_tone_set(s, doc, ns, cur);
363 xmlFreeDoc(doc);
364 return s;
365 }
366 /*endif*/
367 }
368 /*endif*/
369 }
370 /*endif*/
371 cur = cur->next;
372 }
373 /*endwhile*/
374 xmlFreeDoc(doc);
375 free(s->tone_detector);
376 return NULL;
377 }
378 /*- End of function --------------------------------------------------------*/
379 /*- End of file ------------------------------------------------------------*/
380