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