6b2095216fa6feee9236119a3fa2fb918330c128
[asterisk/asterisk.git] / indications.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2002, Pauline Middelink
5  *
6  * Pauline Middelink <middelink@polyware.nl>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*
20  *
21  * Tone Management
22  * 
23  * This set of function allow us to play a list of tones on a channel.
24  * Each element has two frequencies, which are mixed together and a
25  * duration. For silence both frequencies can be set to 0.
26  * The playtones can be given as a comma separated string.
27  *
28  */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <math.h>                       /* For PI */
34
35 #include "asterisk.h"
36
37 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
38
39 #include "asterisk/indications.h"
40 #include "asterisk/frame.h"
41 #include "asterisk/options.h"
42 #include "asterisk/channel.h"
43 #include "asterisk/logger.h"
44 #include "asterisk/lock.h"
45 #include "asterisk/utils.h"
46
47 static int midi_tohz[128] = {
48                         8,8,9,9,10,10,11,12,12,13,14,
49                         15,16,17,18,19,20,21,23,24,25,
50                         27,29,30,32,34,36,38,41,43,46,
51                         48,51,55,58,61,65,69,73,77,82,
52                         87,92,97,103,110,116,123,130,138,146,
53                         155,164,174,184,195,207,220,233,246,261,
54                         277,293,311,329,349,369,391,415,440,466,
55                         493,523,554,587,622,659,698,739,783,830,
56                         880,932,987,1046,1108,1174,1244,1318,1396,1479,
57                         1567,1661,1760,1864,1975,2093,2217,2349,2489,2637,
58                         2793,2959,3135,3322,3520,3729,3951,4186,4434,4698,
59                         4978,5274,5587,5919,6271,6644,7040,7458,7902,8372,
60                         8869,9397,9956,10548,11175,11839,12543
61                         };
62
63 struct playtones_item {
64         int fac1;
65         int init_v2_1;
66         int init_v3_1;
67         int fac2;
68         int init_v2_2;
69         int init_v3_2;
70         int modulate;
71         int duration;
72 };
73
74 struct playtones_def {
75         int vol;
76         int reppos;
77         int nitems;
78         int interruptible;
79         struct playtones_item *items;
80 };
81
82 struct playtones_state {
83         int vol;
84         int v1_1;
85         int v2_1;
86         int v3_1;
87         int v1_2;
88         int v2_2;
89         int v3_2;
90         int reppos;
91         int nitems;
92         struct playtones_item *items;
93         int npos;
94         int oldnpos;
95         int pos;
96         int origwfmt;
97         struct ast_frame f;
98         unsigned char offset[AST_FRIENDLY_OFFSET];
99         short data[4000];
100 };
101
102 static void playtones_release(struct ast_channel *chan, void *params)
103 {
104         struct playtones_state *ps = params;
105         if (chan) {
106                 ast_set_write_format(chan, ps->origwfmt);
107         }
108         if (ps->items) free(ps->items);
109         free(ps);
110 }
111
112 static void * playtones_alloc(struct ast_channel *chan, void *params)
113 {
114         struct playtones_def *pd = params;
115         struct playtones_state *ps = malloc(sizeof(struct playtones_state));
116         if (!ps)
117                 return NULL;
118         memset(ps, 0, sizeof(struct playtones_state));
119         ps->origwfmt = chan->writeformat;
120         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
121                 ast_log(LOG_WARNING, "Unable to set '%s' to signed linear format (write)\n", chan->name);
122                 playtones_release(NULL, ps);
123                 ps = NULL;
124         } else {
125                 ps->vol = pd->vol;
126                 ps->reppos = pd->reppos;
127                 ps->nitems = pd->nitems;
128                 ps->items = pd->items;
129                 ps->oldnpos = -1;
130         }
131         /* Let interrupts interrupt :) */
132         if (pd->interruptible)
133                 ast_set_flag(chan, AST_FLAG_WRITE_INT);
134         else
135                 ast_clear_flag(chan, AST_FLAG_WRITE_INT);
136         return ps;
137 }
138
139 static int playtones_generator(struct ast_channel *chan, void *data, int len, int samples)
140 {
141         struct playtones_state *ps = data;
142         struct playtones_item *pi;
143         int x;
144         /* we need to prepare a frame with 16 * timelen samples as we're 
145          * generating SLIN audio
146          */
147         len = samples * 2;
148         if (len > sizeof(ps->data) / 2 - 1) {
149                 ast_log(LOG_WARNING, "Can't generate that much data!\n");
150                 return -1;
151         }
152         memset(&ps->f, 0, sizeof(ps->f));
153
154         pi = &ps->items[ps->npos];
155         if (ps->oldnpos != ps->npos) {
156                 /* Load new parameters */
157                 ps->v1_1 = 0;
158                 ps->v2_1 = pi->init_v2_1;
159                 ps->v3_1 = pi->init_v3_1;
160                 ps->v1_2 = 0;
161                 ps->v2_2 = pi->init_v2_2;
162                 ps->v3_2 = pi->init_v3_2;
163                 ps->oldnpos = ps->npos;
164         }
165         for (x=0;x<len/2;x++) {
166                 ps->v1_1 = ps->v2_1;
167                 ps->v2_1 = ps->v3_1;
168                 ps->v3_1 = (pi->fac1 * ps->v2_1 >> 15) - ps->v1_1;
169                 
170                 ps->v1_2 = ps->v2_2;
171                 ps->v2_2 = ps->v3_2;
172                 ps->v3_2 = (pi->fac2 * ps->v2_2 >> 15) - ps->v1_2;
173                 if (pi->modulate) {
174                         int p;
175                         p = ps->v3_2 - 32768;
176                         if (p < 0) p = -p;
177                         p = ((p * 9) / 10) + 1;
178                         ps->data[x] = (ps->v3_1 * p) >> 15;
179                 } else
180                         ps->data[x] = ps->v3_1 + ps->v3_2; 
181         }
182         
183         ps->f.frametype = AST_FRAME_VOICE;
184         ps->f.subclass = AST_FORMAT_SLINEAR;
185         ps->f.datalen = len;
186         ps->f.samples = samples;
187         ps->f.offset = AST_FRIENDLY_OFFSET;
188         ps->f.data = ps->data;
189         ps->f.delivery.tv_sec = 0;
190         ps->f.delivery.tv_usec = 0;
191         ast_write(chan, &ps->f);
192
193         ps->pos += x;
194         if (pi->duration && ps->pos >= pi->duration * 8) {      /* item finished? */
195                 ps->pos = 0;                                    /* start new item */
196                 ps->npos++;
197                 if (ps->npos >= ps->nitems) {                   /* last item? */
198                         if (ps->reppos == -1)                   /* repeat set? */
199                                 return -1;
200                         ps->npos = ps->reppos;                  /* redo from top */
201                 }
202         }
203         return 0;
204 }
205
206 static struct ast_generator playtones = {
207         alloc: playtones_alloc,
208         release: playtones_release,
209         generate: playtones_generator,
210 };
211
212 int ast_playtones_start(struct ast_channel *chan, int vol, const char *playlst, int interruptible)
213 {
214         char *s, *data = ast_strdupa(playlst); /* cute */
215         struct playtones_def d = { vol, -1, 0, 1, NULL};
216         char *stringp=NULL;
217         char *separator;
218         if (!data)
219                 return -1;
220         if (vol < 1)
221                 d.vol = 7219; /* Default to -8db */
222
223         d.interruptible = interruptible;
224         
225         stringp=data;
226         /* the stringp/data is not null here */
227         /* check if the data is separated with '|' or with ',' by default */
228         if (strchr(stringp,'|'))
229                 separator = "|";
230         else
231                 separator = ",";
232         s = strsep(&stringp,separator);
233         while (s && *s) {
234                 int freq1, freq2, time, modulate=0, midinote=0;
235
236                 if (s[0]=='!')
237                         s++;
238                 else if (d.reppos == -1)
239                         d.reppos = d.nitems;
240                 if (sscanf(s, "%d+%d/%d", &freq1, &freq2, &time) == 3) {
241                         /* f1+f2/time format */
242                 } else if (sscanf(s, "%d+%d", &freq1, &freq2) == 2) {
243                         /* f1+f2 format */
244                         time = 0;
245                 } else if (sscanf(s, "%d*%d/%d", &freq1, &freq2, &time) == 3) {
246                         /* f1*f2/time format */
247                         modulate = 1;
248                 } else if (sscanf(s, "%d*%d", &freq1, &freq2) == 2) {
249                         /* f1*f2 format */
250                         time = 0;
251                         modulate = 1;
252                 } else if (sscanf(s, "%d/%d", &freq1, &time) == 2) {
253                         /* f1/time format */
254                         freq2 = 0;
255                 } else if (sscanf(s, "%d", &freq1) == 1) {
256                         /* f1 format */
257                         freq2 = 0;
258                         time = 0;
259                 } else if (sscanf(s, "M%d+M%d/%d", &freq1, &freq2, &time) == 3) {
260                         /* Mf1+Mf2/time format */
261                         midinote = 1;
262                 } else if (sscanf(s, "M%d+M%d", &freq1, &freq2) == 2) {
263                         /* Mf1+Mf2 format */
264                         time = 0;
265                         midinote = 1;
266                 } else if (sscanf(s, "M%d*M%d/%d", &freq1, &freq2, &time) == 3) {
267                         /* Mf1*Mf2/time format */
268                         modulate = 1;
269                         midinote = 1;
270                 } else if (sscanf(s, "M%d*M%d", &freq1, &freq2) == 2) {
271                         /* Mf1*Mf2 format */
272                         time = 0;
273                         modulate = 1;
274                         midinote = 1;
275                 } else if (sscanf(s, "M%d/%d", &freq1, &time) == 2) {
276                         /* Mf1/time format */
277                         freq2 = -1;
278                         midinote = 1;
279                 } else if (sscanf(s, "M%d", &freq1) == 1) {
280                         /* Mf1 format */
281                         freq2 = -1;
282                         time = 0;
283                         midinote = 1;
284                 } else {
285                         ast_log(LOG_WARNING,"%s: tone component '%s' of '%s' is no good\n",chan->name,s,playlst);
286                         return -1;
287                 }
288
289                 if (midinote) {
290                         /* midi notes must be between 0 and 127 */
291                         if ((freq1 >= 0) && (freq1 <= 127))
292                                 freq1 = midi_tohz[freq1];
293                         else
294                                 freq1 = 0;
295
296                         if ((freq2 >= 0) && (freq2 <= 127))
297                                 freq2 = midi_tohz[freq2];
298                         else
299                                 freq2 = 0;
300                 }
301
302                 d.items = realloc(d.items,(d.nitems+1)*sizeof(struct playtones_item));
303                 if (d.items == NULL) {
304                         ast_log(LOG_WARNING, "Realloc failed!\n");
305                         return -1;
306                 }
307                 d.items[d.nitems].fac1 = 2.0 * cos(2.0 * M_PI * (freq1 / 8000.0)) * 32768.0;
308                 d.items[d.nitems].init_v2_1 = sin(-4.0 * M_PI * (freq1 / 8000.0)) * d.vol;
309                 d.items[d.nitems].init_v3_1 = sin(-2.0 * M_PI * (freq1 / 8000.0)) * d.vol;
310
311                 d.items[d.nitems].fac2 = 2.0 * cos(2.0 * M_PI * (freq2 / 8000.0)) * 32768.0;
312                 d.items[d.nitems].init_v2_2 = sin(-4.0 * M_PI * (freq2 / 8000.0)) * d.vol;
313                 d.items[d.nitems].init_v3_2 = sin(-2.0 * M_PI * (freq2 / 8000.0)) * d.vol;
314                 d.items[d.nitems].duration = time;
315                 d.items[d.nitems].modulate = modulate;
316                 d.nitems++;
317
318                 s = strsep(&stringp,separator);
319         }
320
321         if (ast_activate_generator(chan, &playtones, &d)) {
322                 free(d.items);
323                 return -1;
324         }
325         return 0;
326 }
327
328 void ast_playtones_stop(struct ast_channel *chan)
329 {
330         ast_deactivate_generator(chan);
331 }
332
333 /*--------------------------------------------*/
334
335 struct tone_zone *tone_zones;
336 static struct tone_zone *current_tonezone;
337
338 /* Protect the tone_zones list (highly unlikely that two things would change
339  * it at the same time, but still! */
340 AST_MUTEX_DEFINE_EXPORTED(tzlock);
341
342 /* Set global indication country */
343 int ast_set_indication_country(const char *country)
344 {
345         if (country) {
346                 struct tone_zone *z = ast_get_indication_zone(country);
347                 if (z) {
348                         if (option_verbose > 2)
349                                 ast_verbose(VERBOSE_PREFIX_3 "Setting default indication country to '%s'\n",country);
350                         current_tonezone = z;
351                         return 0;
352                 }
353         }
354         return 1; /* not found */
355 }
356
357 /* locate tone_zone, given the country. if country == NULL, use the default country */
358 struct tone_zone *ast_get_indication_zone(const char *country)
359 {
360         struct tone_zone *tz;
361         int alias_loop = 0;
362
363         /* we need some tonezone, pick the first */
364         if (country == NULL && current_tonezone)
365                 return current_tonezone;        /* default country? */
366         if (country == NULL && tone_zones)
367                 return tone_zones;              /* any country? */
368         if (country == NULL)
369                 return 0;       /* not a single country insight */
370
371         if (ast_mutex_lock(&tzlock)) {
372                 ast_log(LOG_WARNING, "Unable to lock tone_zones list\n");
373                 return 0;
374         }
375         do {
376                 for (tz=tone_zones; tz; tz=tz->next) {
377                         if (strcasecmp(country,tz->country)==0) {
378                                 /* tone_zone found */
379                                 if (tz->alias && tz->alias[0]) {
380                                         country = tz->alias;
381                                         break;
382                                 }
383                                 ast_mutex_unlock(&tzlock);
384                                 return tz;
385                         }
386                 }
387         } while (++alias_loop<20 && tz);
388         ast_mutex_unlock(&tzlock);
389         if (alias_loop==20)
390                 ast_log(LOG_NOTICE,"Alias loop for '%s' forcefull broken\n",country);
391         /* nothing found, sorry */
392         return 0;
393 }
394
395 /* locate a tone_zone_sound, given the tone_zone. if tone_zone == NULL, use the default tone_zone */
396 struct tone_zone_sound *ast_get_indication_tone(const struct tone_zone *zone, const char *indication)
397 {
398         struct tone_zone_sound *ts;
399
400         /* we need some tonezone, pick the first */
401         if (zone == NULL && current_tonezone)
402                 zone = current_tonezone;        /* default country? */
403         if (zone == NULL && tone_zones)
404                 zone = tone_zones;              /* any country? */
405         if (zone == NULL)
406                 return 0;       /* not a single country insight */
407
408         if (ast_mutex_lock(&tzlock)) {
409                 ast_log(LOG_WARNING, "Unable to lock tone_zones list\n");
410                 return 0;
411         }
412         for (ts=zone->tones; ts; ts=ts->next) {
413                 if (strcasecmp(indication,ts->name)==0) {
414                         /* found indication! */
415                         ast_mutex_unlock(&tzlock);
416                         return ts;
417                 }
418         }
419         /* nothing found, sorry */
420         ast_mutex_unlock(&tzlock);
421         return 0;
422 }
423
424 /* helper function to delete a tone_zone in its entirety */
425 static inline void free_zone(struct tone_zone* zone)
426 {
427         while (zone->tones) {
428                 struct tone_zone_sound *tmp = zone->tones->next;
429                 free((void*)zone->tones->name);
430                 free((void*)zone->tones->data);
431                 free(zone->tones);
432                 zone->tones = tmp;
433         }
434         if (zone->ringcadance)
435                 free((void*)zone->ringcadance);
436         free(zone);
437 }
438
439 /*--------------------------------------------*/
440
441 /* add a new country, if country exists, it will be replaced. */
442 int ast_register_indication_country(struct tone_zone *zone)
443 {
444         struct tone_zone *tz,*pz;
445
446         if (ast_mutex_lock(&tzlock)) {
447                 ast_log(LOG_WARNING, "Unable to lock tone_zones list\n");
448                 return -1;
449         }
450         for (pz=NULL,tz=tone_zones; tz; pz=tz,tz=tz->next) {
451                 if (strcasecmp(zone->country,tz->country)==0) {
452                         /* tone_zone already there, replace */
453                         zone->next = tz->next;
454                         if (pz)
455                                 pz->next = zone;
456                         else
457                                 tone_zones = zone;
458                         /* if we are replacing the default zone, re-point it */
459                         if (tz == current_tonezone)
460                                 current_tonezone = zone;
461                         /* now free the previous zone */
462                         free_zone(tz);
463                         ast_mutex_unlock(&tzlock);
464                         return 0;
465                 }
466         }
467         /* country not there, add */
468         zone->next = NULL;
469         if (pz)
470                 pz->next = zone;
471         else
472                 tone_zones = zone;
473         ast_mutex_unlock(&tzlock);
474
475         if (option_verbose > 2)
476                 ast_verbose(VERBOSE_PREFIX_3 "Registered indication country '%s'\n",zone->country);
477         return 0;
478 }
479
480 /* remove an existing country and all its indications, country must exist.
481  * Also, all countries which are an alias for the specified country are removed. */
482 int ast_unregister_indication_country(const char *country)
483 {
484         struct tone_zone *tz, *pz = NULL, *tmp;
485         int res = -1;
486
487         if (ast_mutex_lock(&tzlock)) {
488                 ast_log(LOG_WARNING, "Unable to lock tone_zones list\n");
489                 return -1;
490         }
491         tz = tone_zones;
492         while (tz) {
493                 if (country==NULL ||
494                     (strcasecmp(country, tz->country)==0 ||
495                      strcasecmp(country, tz->alias)==0)) {
496                         /* tone_zone found, remove */
497                         tmp = tz->next;
498                         if (pz)
499                                 pz->next = tmp;
500                         else
501                                 tone_zones = tmp;
502                         /* if we are unregistering the default country, w'll notice */
503                         if (tz == current_tonezone) {
504                                 ast_log(LOG_NOTICE,"Removed default indication country '%s'\n",tz->country);
505                                 current_tonezone = NULL;
506                         }
507                         if (option_verbose > 2)
508                                 ast_verbose(VERBOSE_PREFIX_3 "Unregistered indication country '%s'\n",tz->country);
509                         free_zone(tz);
510                         if (tone_zones == tz)
511                                 tone_zones = tmp;
512                         tz = tmp;
513                         res = 0;
514                 }
515                 else {
516                         /* next zone please */
517                         pz = tz;
518                         tz = tz->next;
519                 }
520         }
521         ast_mutex_unlock(&tzlock);
522         return res;
523 }
524
525 /* add a new indication to a tone_zone. tone_zone must exist. if the indication already
526  * exists, it will be replaced. */
527 int ast_register_indication(struct tone_zone *zone, const char *indication, const char *tonelist)
528 {
529         struct tone_zone_sound *ts,*ps;
530
531         /* is it an alias? stop */
532         if (zone->alias[0])
533                 return -1;
534
535         if (ast_mutex_lock(&tzlock)) {
536                 ast_log(LOG_WARNING, "Unable to lock tone_zones list\n");
537                 return -2;
538         }
539         for (ps=NULL,ts=zone->tones; ts; ps=ts,ts=ts->next) {
540                 if (strcasecmp(indication,ts->name)==0) {
541                         /* indication already there, replace */
542                         free((void*)ts->name);
543                         free((void*)ts->data);
544                         break;
545                 }
546         }
547         if (!ts) {
548                 /* not there, we have to add */
549                 ts = malloc(sizeof(struct tone_zone_sound));
550                 if (!ts) {
551                         ast_log(LOG_WARNING, "Out of memory\n");
552                         ast_mutex_unlock(&tzlock);
553                         return -2;
554                 }
555                 ts->next = NULL;
556         }
557         ts->name = strdup(indication);
558         ts->data = strdup(tonelist);
559         if (ts->name==NULL || ts->data==NULL) {
560                 ast_log(LOG_WARNING, "Out of memory\n");
561                 ast_mutex_unlock(&tzlock);
562                 return -2;
563         }
564         if (ps)
565                 ps->next = ts;
566         else
567                 zone->tones = ts;
568         ast_mutex_unlock(&tzlock);
569         return 0;
570 }
571
572 /* remove an existing country's indication. Both country and indication must exist */
573 int ast_unregister_indication(struct tone_zone *zone, const char *indication)
574 {
575         struct tone_zone_sound *ts,*ps = NULL, *tmp;
576         int res = -1;
577
578         /* is it an alias? stop */
579         if (zone->alias[0])
580                 return -1;
581
582         if (ast_mutex_lock(&tzlock)) {
583                 ast_log(LOG_WARNING, "Unable to lock tone_zones list\n");
584                 return -1;
585         }
586         ts = zone->tones;
587         while (ts) {
588                 if (strcasecmp(indication,ts->name)==0) {
589                         /* indication found */
590                         tmp = ts->next;
591                         if (ps)
592                                 ps->next = tmp;
593                         else
594                                 zone->tones = tmp;
595                         free((void*)ts->name);
596                         free((void*)ts->data);
597                         free(ts);
598                         ts = tmp;
599                         res = 0;
600                 }
601                 else {
602                         /* next zone please */
603                         ps = ts;
604                         ts = ts->next;
605                 }
606         }
607         /* indication not found, goodbye */
608         ast_mutex_unlock(&tzlock);
609         return res;
610 }