merge new_loader_completion branch, including (at least):
[asterisk/asterisk.git] / main / 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 "asterisk.h"
32
33 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <math.h>
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;
117         if (!(ps = ast_calloc(1, sizeof(*ps))))
118                 return NULL;
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;
217         char *separator;
218         
219         if (vol < 1)
220                 d.vol = 7219; /* Default to -8db */
221
222         d.interruptible = interruptible;
223         
224         stringp=data;
225         /* the stringp/data is not null here */
226         /* check if the data is separated with '|' or with ',' by default */
227         if (strchr(stringp,'|'))
228                 separator = "|";
229         else
230                 separator = ",";
231         s = strsep(&stringp,separator);
232         while (s && *s) {
233                 int freq1, freq2, time, modulate=0, midinote=0;
234
235                 if (s[0]=='!')
236                         s++;
237                 else if (d.reppos == -1)
238                         d.reppos = d.nitems;
239                 if (sscanf(s, "%d+%d/%d", &freq1, &freq2, &time) == 3) {
240                         /* f1+f2/time format */
241                 } else if (sscanf(s, "%d+%d", &freq1, &freq2) == 2) {
242                         /* f1+f2 format */
243                         time = 0;
244                 } else if (sscanf(s, "%d*%d/%d", &freq1, &freq2, &time) == 3) {
245                         /* f1*f2/time format */
246                         modulate = 1;
247                 } else if (sscanf(s, "%d*%d", &freq1, &freq2) == 2) {
248                         /* f1*f2 format */
249                         time = 0;
250                         modulate = 1;
251                 } else if (sscanf(s, "%d/%d", &freq1, &time) == 2) {
252                         /* f1/time format */
253                         freq2 = 0;
254                 } else if (sscanf(s, "%d", &freq1) == 1) {
255                         /* f1 format */
256                         freq2 = 0;
257                         time = 0;
258                 } else if (sscanf(s, "M%d+M%d/%d", &freq1, &freq2, &time) == 3) {
259                         /* Mf1+Mf2/time format */
260                         midinote = 1;
261                 } else if (sscanf(s, "M%d+M%d", &freq1, &freq2) == 2) {
262                         /* Mf1+Mf2 format */
263                         time = 0;
264                         midinote = 1;
265                 } else if (sscanf(s, "M%d*M%d/%d", &freq1, &freq2, &time) == 3) {
266                         /* Mf1*Mf2/time format */
267                         modulate = 1;
268                         midinote = 1;
269                 } else if (sscanf(s, "M%d*M%d", &freq1, &freq2) == 2) {
270                         /* Mf1*Mf2 format */
271                         time = 0;
272                         modulate = 1;
273                         midinote = 1;
274                 } else if (sscanf(s, "M%d/%d", &freq1, &time) == 2) {
275                         /* Mf1/time format */
276                         freq2 = -1;
277                         midinote = 1;
278                 } else if (sscanf(s, "M%d", &freq1) == 1) {
279                         /* Mf1 format */
280                         freq2 = -1;
281                         time = 0;
282                         midinote = 1;
283                 } else {
284                         ast_log(LOG_WARNING,"%s: tone component '%s' of '%s' is no good\n",chan->name,s,playlst);
285                         return -1;
286                 }
287
288                 if (midinote) {
289                         /* midi notes must be between 0 and 127 */
290                         if ((freq1 >= 0) && (freq1 <= 127))
291                                 freq1 = midi_tohz[freq1];
292                         else
293                                 freq1 = 0;
294
295                         if ((freq2 >= 0) && (freq2 <= 127))
296                                 freq2 = midi_tohz[freq2];
297                         else
298                                 freq2 = 0;
299                 }
300
301                 if (!(d.items = ast_realloc(d.items, (d.nitems + 1) * sizeof(*d.items)))) {
302                         return -1;
303                 }
304                 d.items[d.nitems].fac1 = 2.0 * cos(2.0 * M_PI * (freq1 / 8000.0)) * 32768.0;
305                 d.items[d.nitems].init_v2_1 = sin(-4.0 * M_PI * (freq1 / 8000.0)) * d.vol;
306                 d.items[d.nitems].init_v3_1 = sin(-2.0 * M_PI * (freq1 / 8000.0)) * d.vol;
307
308                 d.items[d.nitems].fac2 = 2.0 * cos(2.0 * M_PI * (freq2 / 8000.0)) * 32768.0;
309                 d.items[d.nitems].init_v2_2 = sin(-4.0 * M_PI * (freq2 / 8000.0)) * d.vol;
310                 d.items[d.nitems].init_v3_2 = sin(-2.0 * M_PI * (freq2 / 8000.0)) * d.vol;
311                 d.items[d.nitems].duration = time;
312                 d.items[d.nitems].modulate = modulate;
313                 d.nitems++;
314
315                 s = strsep(&stringp,separator);
316         }
317
318         if (ast_activate_generator(chan, &playtones, &d)) {
319                 free(d.items);
320                 return -1;
321         }
322         return 0;
323 }
324
325 void ast_playtones_stop(struct ast_channel *chan)
326 {
327         ast_deactivate_generator(chan);
328 }
329
330 /*--------------------------------------------*/
331
332 static struct tone_zone *tone_zones;
333 static struct tone_zone *current_tonezone;
334
335 /* Protect the tone_zones list (highly unlikely that two things would change
336  * it at the same time, but still! */
337 AST_MUTEX_DEFINE_STATIC(tzlock);
338
339 struct tone_zone *ast_walk_indications(const struct tone_zone *cur)
340 {
341         struct tone_zone *tz;
342
343         if (cur == NULL)
344                 return tone_zones;
345         ast_mutex_lock(&tzlock);
346         for (tz = tone_zones; tz; tz = tz->next)
347                 if (tz == cur)
348                         break;
349         if (tz)
350                 tz = tz->next;
351         ast_mutex_unlock(&tzlock);
352         return tz;
353 }
354
355 /* Set global indication country */
356 int ast_set_indication_country(const char *country)
357 {
358         if (country) {
359                 struct tone_zone *z = ast_get_indication_zone(country);
360                 if (z) {
361                         if (option_verbose > 2)
362                                 ast_verbose(VERBOSE_PREFIX_3 "Setting default indication country to '%s'\n",country);
363                         current_tonezone = z;
364                         return 0;
365                 }
366         }
367         return 1; /* not found */
368 }
369
370 /* locate tone_zone, given the country. if country == NULL, use the default country */
371 struct tone_zone *ast_get_indication_zone(const char *country)
372 {
373         struct tone_zone *tz;
374         int alias_loop = 0;
375
376         /* we need some tonezone, pick the first */
377         if (country == NULL && current_tonezone)
378                 return current_tonezone;        /* default country? */
379         if (country == NULL && tone_zones)
380                 return tone_zones;              /* any country? */
381         if (country == NULL)
382                 return 0;       /* not a single country insight */
383
384         ast_mutex_lock(&tzlock);
385         do {
386                 for (tz=tone_zones; tz; tz=tz->next) {
387                         if (strcasecmp(country,tz->country)==0) {
388                                 /* tone_zone found */
389                                 if (tz->alias && tz->alias[0]) {
390                                         country = tz->alias;
391                                         break;
392                                 }
393                                 ast_mutex_unlock(&tzlock);
394                                 return tz;
395                         }
396                 }
397         } while (++alias_loop<20 && tz);
398         ast_mutex_unlock(&tzlock);
399         if (alias_loop==20)
400                 ast_log(LOG_NOTICE,"Alias loop for '%s' forcefull broken\n",country);
401         /* nothing found, sorry */
402         return 0;
403 }
404
405 /* locate a tone_zone_sound, given the tone_zone. if tone_zone == NULL, use the default tone_zone */
406 struct tone_zone_sound *ast_get_indication_tone(const struct tone_zone *zone, const char *indication)
407 {
408         struct tone_zone_sound *ts;
409
410         /* we need some tonezone, pick the first */
411         if (zone == NULL && current_tonezone)
412                 zone = current_tonezone;        /* default country? */
413         if (zone == NULL && tone_zones)
414                 zone = tone_zones;              /* any country? */
415         if (zone == NULL)
416                 return 0;       /* not a single country insight */
417
418         ast_mutex_lock(&tzlock);
419         for (ts=zone->tones; ts; ts=ts->next) {
420                 if (strcasecmp(indication,ts->name)==0) {
421                         /* found indication! */
422                         ast_mutex_unlock(&tzlock);
423                         return ts;
424                 }
425         }
426         /* nothing found, sorry */
427         ast_mutex_unlock(&tzlock);
428         return 0;
429 }
430
431 /* helper function to delete a tone_zone in its entirety */
432 static inline void free_zone(struct tone_zone* zone)
433 {
434         while (zone->tones) {
435                 struct tone_zone_sound *tmp = zone->tones->next;
436                 free((void*)zone->tones->name);
437                 free((void*)zone->tones->data);
438                 free(zone->tones);
439                 zone->tones = tmp;
440         }
441         if (zone->ringcadence)
442                 free(zone->ringcadence);
443         free(zone);
444 }
445
446 /*--------------------------------------------*/
447
448 /* add a new country, if country exists, it will be replaced. */
449 int ast_register_indication_country(struct tone_zone *zone)
450 {
451         struct tone_zone *tz,*pz;
452
453         ast_mutex_lock(&tzlock);
454         for (pz=NULL,tz=tone_zones; tz; pz=tz,tz=tz->next) {
455                 if (strcasecmp(zone->country,tz->country)==0) {
456                         /* tone_zone already there, replace */
457                         zone->next = tz->next;
458                         if (pz)
459                                 pz->next = zone;
460                         else
461                                 tone_zones = zone;
462                         /* if we are replacing the default zone, re-point it */
463                         if (tz == current_tonezone)
464                                 current_tonezone = zone;
465                         /* now free the previous zone */
466                         free_zone(tz);
467                         ast_mutex_unlock(&tzlock);
468                         return 0;
469                 }
470         }
471         /* country not there, add */
472         zone->next = NULL;
473         if (pz)
474                 pz->next = zone;
475         else
476                 tone_zones = zone;
477         ast_mutex_unlock(&tzlock);
478
479         if (option_verbose > 2)
480                 ast_verbose(VERBOSE_PREFIX_3 "Registered indication country '%s'\n",zone->country);
481         return 0;
482 }
483
484 /* remove an existing country and all its indications, country must exist.
485  * Also, all countries which are an alias for the specified country are removed. */
486 int ast_unregister_indication_country(const char *country)
487 {
488         struct tone_zone *tz, *pz = NULL, *tmp;
489         int res = -1;
490
491         ast_mutex_lock(&tzlock);
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         ast_mutex_lock(&tzlock);
537         for (ps=NULL,ts=zone->tones; ts; ps=ts,ts=ts->next) {
538                 if (strcasecmp(indication,ts->name)==0) {
539                         /* indication already there, replace */
540                         free((void*)ts->name);
541                         free((void*)ts->data);
542                         break;
543                 }
544         }
545         if (!ts) {
546                 /* not there, we have to add */
547                 if (!(ts = ast_malloc(sizeof(*ts)))) {
548                         ast_mutex_unlock(&tzlock);
549                         return -2;
550                 }
551                 ts->next = NULL;
552         }
553         if (!(ts->name = ast_strdup(indication)) || !(ts->data = ast_strdup(tonelist))) {
554                 ast_mutex_unlock(&tzlock);
555                 return -2;
556         }
557         if (ps)
558                 ps->next = ts;
559         else
560                 zone->tones = ts;
561         ast_mutex_unlock(&tzlock);
562         return 0;
563 }
564
565 /* remove an existing country's indication. Both country and indication must exist */
566 int ast_unregister_indication(struct tone_zone *zone, const char *indication)
567 {
568         struct tone_zone_sound *ts,*ps = NULL, *tmp;
569         int res = -1;
570
571         /* is it an alias? stop */
572         if (zone->alias[0])
573                 return -1;
574
575         ast_mutex_lock(&tzlock);
576         ts = zone->tones;
577         while (ts) {
578                 if (strcasecmp(indication,ts->name)==0) {
579                         /* indication found */
580                         tmp = ts->next;
581                         if (ps)
582                                 ps->next = tmp;
583                         else
584                                 zone->tones = tmp;
585                         free((void*)ts->name);
586                         free((void*)ts->data);
587                         free(ts);
588                         ts = tmp;
589                         res = 0;
590                 }
591                 else {
592                         /* next zone please */
593                         ps = ts;
594                         ts = ts->next;
595                 }
596         }
597         /* indication not found, goodbye */
598         ast_mutex_unlock(&tzlock);
599         return res;
600 }