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