2 * Asterisk -- A telephony toolkit for Linux.
6 * Copyright (C) 2002, Pauline Middelink
8 * Pauline Middelink <middelink@polyware.nl>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
13 * This set of function allow us to play a list of tones on a channel.
14 * Each element has two frequencies, which are mixed together and a
15 * duration. For silence both frequencies can be set to 0.
16 * The playtones can be given as a comma seperated string.
23 #include <math.h> /* For PI */
24 #include <asterisk/indications.h>
25 #include <asterisk/frame.h>
26 #include <asterisk/options.h>
27 #include <asterisk/channel.h>
28 #include <asterisk/logger.h>
30 struct playtones_item {
37 struct playtones_def {
42 struct playtones_item *items;
45 struct playtones_state {
49 struct playtones_item *items;
57 static void playtones_release(struct ast_channel *chan, void *params)
59 struct playtones_state *ps = params;
61 ast_set_write_format(chan, ps->origwfmt);
63 if (ps->items) free(ps->items);
67 static void * playtones_alloc(struct ast_channel *chan, void *params)
69 struct playtones_def *pd = params;
70 struct playtones_state *ps = malloc(sizeof(struct playtones_state));
73 memset(ps, 0, sizeof(struct playtones_state));
74 ps->origwfmt = chan->writeformat;
75 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
76 ast_log(LOG_WARNING, "Unable to set '%s' to signed linear format (write)\n", chan->name);
77 playtones_release(NULL, ps);
81 ps->reppos = pd->reppos;
82 ps->nitems = pd->nitems;
83 ps->items = pd->items;
85 /* Let interrupts interrupt :) */
86 chan->writeinterrupt = pd->interruptible;
90 static int playtones_generator(struct ast_channel *chan, void *data, int len, int samples)
92 struct playtones_state *ps = data;
93 struct playtones_item *pi;
95 /* we need to prepare a frame with 16 * timelen samples as we're
96 * generating SLIN audio
99 if (len > sizeof(ps->data) / 2 - 1) {
100 ast_log(LOG_WARNING, "Can't generate that much data!\n");
103 memset(&ps->f, 0, sizeof(ps->f));
105 pi = &ps->items[ps->npos];
106 for (x=0;x<len/2;x++) {
108 /* Modulate 1st tone with 2nd, to 90% modulation depth */
109 ps->data[x] = ps->vol * 2 * (
110 sin((pi->freq1 * 2.0 * M_PI / 8000.0) * (ps->pos + x)) *
111 (0.9 * fabs(sin((pi->freq2 * 2.0 * M_PI / 8000.0) * (ps->pos + x))) + 0.1)
114 /* Add 2 tones together */
115 ps->data[x] = ps->vol * (
116 sin((pi->freq1 * 2.0 * M_PI / 8000.0) * (ps->pos + x)) +
117 sin((pi->freq2 * 2.0 * M_PI / 8000.0) * (ps->pos + x))
120 ps->f.frametype = AST_FRAME_VOICE;
121 ps->f.subclass = AST_FORMAT_SLINEAR;
123 ps->f.samples = samples;
124 ps->f.offset = AST_FRIENDLY_OFFSET;
125 ps->f.data = ps->data;
126 ast_write(chan, &ps->f);
129 if (pi->duration && ps->pos >= pi->duration * 8) { /* item finished? */
130 ps->pos = 0; /* start new item */
132 if (ps->npos >= ps->nitems) { /* last item? */
133 if (ps->reppos == -1) /* repeat set? */
135 ps->npos = ps->reppos; /* redo from top */
141 static struct ast_generator playtones = {
142 alloc: playtones_alloc,
143 release: playtones_release,
144 generate: playtones_generator,
147 int ast_playtones_start(struct ast_channel *chan, int vol, const char *playlst, int interruptible)
149 char *s, *data = ast_strdupa(playlst); /* cute */
150 struct playtones_def d = { vol, -1, 0, 1, NULL};
158 d.interruptible = interruptible;
161 /* the stringp/data is not null here */
162 /* check if the data is separated with '|' or with ',' by default */
163 if (strchr(stringp,'|'))
167 s = strsep(&stringp,separator);
169 int freq1, freq2, time, modulate=0;
173 else if (d.reppos == -1)
175 if (sscanf(s, "%d+%d/%d", &freq1, &freq2, &time) == 3) {
176 /* f1+f2/time format */
177 } else if (sscanf(s, "%d+%d", &freq1, &freq2) == 2) {
180 } else if (sscanf(s, "%d*%d/%d", &freq1, &freq2, &time) == 3) {
181 /* f1*f2/time format */
183 } else if (sscanf(s, "%d*%d", &freq1, &freq2) == 2) {
187 } else if (sscanf(s, "%d/%d", &freq1, &time) == 2) {
190 } else if (sscanf(s, "%d", &freq1) == 1) {
195 ast_log(LOG_WARNING,"%s: tone component '%s' of '%s' is no good\n",chan->name,s,playlst);
199 d.items = realloc(d.items,(d.nitems+1)*sizeof(struct playtones_item));
202 d.items[d.nitems].freq1 = freq1;
203 d.items[d.nitems].freq2 = freq2;
204 d.items[d.nitems].duration = time;
205 d.items[d.nitems].modulate = modulate;
208 s = strsep(&stringp,separator);
211 if (ast_activate_generator(chan, &playtones, &d)) {
218 void ast_playtones_stop(struct ast_channel *chan)
220 ast_deactivate_generator(chan);
223 /*--------------------------------------------*/
225 struct tone_zone *tone_zones;
226 static struct tone_zone *current_tonezone;
228 /* Protect the tone_zones list (highly unlikely that two things would change
229 * it at the same time, but still! */
230 ast_mutex_t tzlock = AST_MUTEX_INITIALIZER;
232 /* Set global indication country */
233 int ast_set_indication_country(const char *country)
236 struct tone_zone *z = ast_get_indication_zone(country);
238 if (option_verbose > 2)
239 ast_verbose(VERBOSE_PREFIX_3 "Setting default indication country to '%s'\n",country);
240 current_tonezone = z;
244 return 1; /* not found */
247 /* locate tone_zone, given the country. if country == NULL, use the default country */
248 struct tone_zone *ast_get_indication_zone(const char *country)
250 struct tone_zone *tz;
253 /* we need some tonezone, pick the first */
254 if (country == NULL && current_tonezone)
255 return current_tonezone; /* default country? */
256 if (country == NULL && tone_zones)
257 return tone_zones; /* any country? */
259 return 0; /* not a single country insight */
261 if (ast_mutex_lock(&tzlock)) {
262 ast_log(LOG_WARNING, "Unable to lock tone_zones list\n");
266 for (tz=tone_zones; tz; tz=tz->next) {
267 if (strcasecmp(country,tz->country)==0) {
268 /* tone_zone found */
269 if (tz->alias && tz->alias[0]) {
273 ast_mutex_unlock(&tzlock);
277 } while (++alias_loop<20 && tz);
278 ast_mutex_unlock(&tzlock);
280 ast_log(LOG_NOTICE,"Alias loop for '%s' forcefull broken\n",country);
281 /* nothing found, sorry */
285 /* locate a tone_zone_sound, given the tone_zone. if tone_zone == NULL, use the default tone_zone */
286 struct tone_zone_sound *ast_get_indication_tone(const struct tone_zone *zone, const char *indication)
288 struct tone_zone_sound *ts;
290 /* we need some tonezone, pick the first */
291 if (zone == NULL && current_tonezone)
292 zone = current_tonezone; /* default country? */
293 if (zone == NULL && tone_zones)
294 zone = tone_zones; /* any country? */
296 return 0; /* not a single country insight */
298 if (ast_mutex_lock(&tzlock)) {
299 ast_log(LOG_WARNING, "Unable to lock tone_zones list\n");
302 for (ts=zone->tones; ts; ts=ts->next) {
303 if (strcasecmp(indication,ts->name)==0) {
304 /* found indication! */
305 ast_mutex_unlock(&tzlock);
309 /* nothing found, sorry */
310 ast_mutex_unlock(&tzlock);
314 /* helper function to delete a tone_zone in its entirety */
315 static inline void free_zone(struct tone_zone* zone)
317 while (zone->tones) {
318 struct tone_zone_sound *tmp = zone->tones->next;
319 free((void*)zone->tones->name);
320 free((void*)zone->tones->data);
321 free((void*)zone->ringcadance);
328 /*--------------------------------------------*/
330 /* add a new country, if country exists, it will be replaced. */
331 int ast_register_indication_country(struct tone_zone *zone)
333 struct tone_zone *tz,*pz;
335 if (ast_mutex_lock(&tzlock)) {
336 ast_log(LOG_WARNING, "Unable to lock tone_zones list\n");
339 for (pz=NULL,tz=tone_zones; tz; pz=tz,tz=tz->next) {
340 if (strcasecmp(zone->country,tz->country)==0) {
341 /* tone_zone already there, replace */
342 zone->next = tz->next;
347 /* if we are replacing the default zone, re-point it */
348 if (tz == current_tonezone)
349 current_tonezone = zone;
350 /* now free the previous zone */
352 ast_mutex_unlock(&tzlock);
356 /* country not there, add */
362 ast_mutex_unlock(&tzlock);
364 if (option_verbose > 2)
365 ast_verbose(VERBOSE_PREFIX_3 "Registered indication country '%s'\n",zone->country);
369 /* remove an existing country and all its indications, country must exist.
370 * Also, all countries which are an alias for the specified country are removed. */
371 int ast_unregister_indication_country(const char *country)
373 struct tone_zone *tz, *pz = NULL, *tmp;
376 if (ast_mutex_lock(&tzlock)) {
377 ast_log(LOG_WARNING, "Unable to lock tone_zones list\n");
383 (strcasecmp(country, tz->country)==0 ||
384 strcasecmp(country, tz->alias)==0)) {
385 /* tone_zone found, remove */
391 /* if we are unregistering the default country, w'll notice */
392 if (tz == current_tonezone) {
393 ast_log(LOG_NOTICE,"Removed default indication country '%s'\n",tz->country);
394 current_tonezone = NULL;
396 if (option_verbose > 2)
397 ast_verbose(VERBOSE_PREFIX_3 "Unregistered indication country '%s'\n",tz->country);
399 if (tone_zones == tz)
405 /* next zone please */
410 ast_mutex_unlock(&tzlock);
414 /* add a new indication to a tone_zone. tone_zone must exist. if the indication already
415 * exists, it will be replaced. */
416 int ast_register_indication(struct tone_zone *zone, const char *indication, const char *tonelist)
418 struct tone_zone_sound *ts,*ps;
420 /* is it an alias? stop */
424 if (ast_mutex_lock(&tzlock)) {
425 ast_log(LOG_WARNING, "Unable to lock tone_zones list\n");
428 for (ps=NULL,ts=zone->tones; ts; ps=ts,ts=ts->next) {
429 if (strcasecmp(indication,ts->name)==0) {
430 /* indication already there, replace */
431 free((void*)ts->name);
432 free((void*)ts->data);
437 /* not there, we have to add */
438 ts = malloc(sizeof(struct tone_zone_sound));
440 ast_log(LOG_WARNING, "Out of memory\n");
441 ast_mutex_unlock(&tzlock);
446 ts->name = strdup(indication);
447 ts->data = strdup(tonelist);
448 if (ts->name==NULL || ts->data==NULL) {
449 ast_log(LOG_WARNING, "Out of memory\n");
450 ast_mutex_unlock(&tzlock);
457 ast_mutex_unlock(&tzlock);
461 /* remove an existing country's indication. Both country and indication must exist */
462 int ast_unregister_indication(struct tone_zone *zone, const char *indication)
464 struct tone_zone_sound *ts,*ps = NULL, *tmp;
467 /* is it an alias? stop */
471 if (ast_mutex_lock(&tzlock)) {
472 ast_log(LOG_WARNING, "Unable to lock tone_zones list\n");
477 if (strcasecmp(indication,ts->name)==0) {
478 /* indication found */
484 free((void*)ts->name);
485 free((void*)ts->data);
491 /* next zone please */
496 /* indication not found, goodbye */
497 ast_mutex_unlock(&tzlock);