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 #define PTHREAD_MUTEX_LOCK(a) ast_pthread_mutex_lock(a)
31 #define PTHREAD_MUTEX_UNLOCK(a) ast_pthread_mutex_unlock(a)
33 struct playtones_item {
39 struct playtones_def {
44 struct playtones_item *items;
47 struct playtones_state {
51 struct playtones_item *items;
59 static void playtones_release(struct ast_channel *chan, void *params)
61 struct playtones_state *ps = params;
63 ast_set_write_format(chan, ps->origwfmt);
65 if (ps->items) free(ps->items);
69 static void * playtones_alloc(struct ast_channel *chan, void *params)
71 struct playtones_def *pd = params;
72 struct playtones_state *ps = malloc(sizeof(struct playtones_state));
75 memset(ps, 0, sizeof(struct playtones_state));
76 ps->origwfmt = chan->writeformat;
77 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
78 ast_log(LOG_WARNING, "Unable to set '%s' to signed linear format (write)\n", chan->name);
79 playtones_release(NULL, ps);
83 ps->reppos = pd->reppos;
84 ps->nitems = pd->nitems;
85 ps->items = pd->items;
87 /* Let interrupts interrupt :) */
88 chan->writeinterrupt = pd->interruptible;
92 static int playtones_generator(struct ast_channel *chan, void *data, int len, int samples)
94 struct playtones_state *ps = data;
95 struct playtones_item *pi;
97 /* we need to prepare a frame with 16 * timelen samples as we're
98 * generating SLIN audio
101 if (len > sizeof(ps->data) / 2 - 1) {
102 ast_log(LOG_WARNING, "Can't generate that much data!\n");
105 memset(&ps->f, 0, sizeof(ps->f));
107 pi = &ps->items[ps->npos];
108 for (x=0;x<len/2;x++) {
109 ps->data[x] = ps->vol * (
110 sin((pi->freq1 * 2.0 * M_PI / 8000.0) * (ps->pos + x)) +
111 sin((pi->freq2 * 2.0 * M_PI / 8000.0) * (ps->pos + x))
114 ps->f.frametype = AST_FRAME_VOICE;
115 ps->f.subclass = AST_FORMAT_SLINEAR;
117 ps->f.samples = samples;
118 ps->f.offset = AST_FRIENDLY_OFFSET;
119 ps->f.data = ps->data;
120 ast_write(chan, &ps->f);
123 if (pi->duration && ps->pos >= pi->duration * 8) { /* item finished? */
124 ps->pos = 0; /* start new item */
126 if (ps->npos >= ps->nitems) { /* last item? */
127 if (ps->reppos == -1) /* repeat set? */
129 ps->npos = ps->reppos; /* redo from top */
135 static struct ast_generator playtones = {
136 alloc: playtones_alloc,
137 release: playtones_release,
138 generate: playtones_generator,
141 int ast_playtones_start(struct ast_channel *chan, int vol, const char *playlst, int interruptible)
143 char *s, *data = strdupa(playlst); /* cute */
144 struct playtones_def d = { vol, -1, 0, 1, NULL};
151 d.interruptible = interruptible;
154 s = strsep(&stringp,",");
156 int freq1, freq2, time;
160 else if (d.reppos == -1)
162 if (sscanf(s, "%d+%d/%d", &freq1, &freq2, &time) == 3) {
163 /* f1+f2/time format */
164 } else if (sscanf(s, "%d+%d", &freq1, &freq2) == 2) {
167 } else if (sscanf(s, "%d/%d", &freq1, &time) == 2) {
170 } else if (sscanf(s, "%d", &freq1) == 1) {
175 ast_log(LOG_WARNING,"%s: tone component '%s' of '%s' is no good\n",chan->name,s,playlst);
179 d.items = realloc(d.items,(d.nitems+1)*sizeof(struct playtones_item));
182 d.items[d.nitems].freq1 = freq1;
183 d.items[d.nitems].freq2 = freq2;
184 d.items[d.nitems].duration = time;
187 s = strsep(&stringp,",");
190 if (ast_activate_generator(chan, &playtones, &d)) {
197 void ast_playtones_stop(struct ast_channel *chan)
199 ast_deactivate_generator(chan);
202 /*--------------------------------------------*/
204 struct tone_zone *tone_zones;
205 static struct tone_zone *current_tonezone;
207 /* Protect the tone_zones list (highly unlikely that two things would change
208 * it at the same time, but still! */
209 pthread_mutex_t tzlock = AST_MUTEX_INITIALIZER;
211 /* Set global indication country */
212 int ast_set_indication_country(const char *country)
215 struct tone_zone *z = ast_get_indication_zone(country);
217 ast_verbose(VERBOSE_PREFIX_3 "Setting default indication country to '%s'\n",country);
218 current_tonezone = z;
222 return 1; /* not found */
225 /* locate tone_zone, given the country. if country == NULL, use the default country */
226 struct tone_zone *ast_get_indication_zone(const char *country)
228 struct tone_zone *tz;
231 /* we need some tonezone, pick the first */
232 if (country == NULL && current_tonezone)
233 return current_tonezone; /* default country? */
234 if (country == NULL && tone_zones)
235 return tone_zones; /* any country? */
237 return 0; /* not a single country insight */
239 if (PTHREAD_MUTEX_LOCK(&tzlock)) {
240 ast_log(LOG_WARNING, "Unable to lock tone_zones list\n");
244 for (tz=tone_zones; tz; tz=tz->next) {
245 if (strcasecmp(country,tz->country)==0) {
246 /* tone_zone found */
247 if (tz->alias && tz->alias[0]) {
251 PTHREAD_MUTEX_UNLOCK(&tzlock);
255 } while (++alias_loop<20 && tz);
256 PTHREAD_MUTEX_UNLOCK(&tzlock);
258 ast_log(LOG_NOTICE,"Alias loop for '%s' forcefull broken\n",country);
259 /* nothing found, sorry */
263 /* locate a tone_zone_sound, given the tone_zone. if tone_zone == NULL, use the default tone_zone */
264 struct tone_zone_sound *ast_get_indication_tone(const struct tone_zone *zone, const char *indication)
266 struct tone_zone_sound *ts;
268 /* we need some tonezone, pick the first */
269 if (zone == NULL && current_tonezone)
270 zone = current_tonezone; /* default country? */
271 if (zone == NULL && tone_zones)
272 zone = tone_zones; /* any country? */
274 return 0; /* not a single country insight */
276 if (PTHREAD_MUTEX_LOCK(&tzlock)) {
277 ast_log(LOG_WARNING, "Unable to lock tone_zones list\n");
280 for (ts=zone->tones; ts; ts=ts->next) {
281 if (strcasecmp(indication,ts->name)==0) {
282 /* found indication! */
283 PTHREAD_MUTEX_UNLOCK(&tzlock);
287 /* nothing found, sorry */
288 PTHREAD_MUTEX_UNLOCK(&tzlock);
292 /* helper function to delete a tone_zone in its entirety */
293 static inline void free_zone(struct tone_zone* zone)
295 while (zone->tones) {
296 struct tone_zone_sound *tmp = zone->tones->next;
297 free((void*)zone->tones->name);
298 free((void*)zone->tones->data);
305 /*--------------------------------------------*/
307 /* add a new country, if country exists, it will be replaced. */
308 int ast_register_indication_country(struct tone_zone *zone)
310 struct tone_zone *tz,*pz;
312 if (PTHREAD_MUTEX_LOCK(&tzlock)) {
313 ast_log(LOG_WARNING, "Unable to lock tone_zones list\n");
316 for (pz=NULL,tz=tone_zones; tz; pz=tz,tz=tz->next) {
317 if (strcasecmp(zone->country,tz->country)==0) {
318 /* tone_zone already there, replace */
319 zone->next = tz->next;
324 /* if we are replacing the default zone, re-point it */
325 if (tz == current_tonezone)
326 current_tonezone = zone;
327 /* now free the previous zone */
329 PTHREAD_MUTEX_UNLOCK(&tzlock);
333 /* country not there, add */
339 PTHREAD_MUTEX_UNLOCK(&tzlock);
341 ast_verbose(VERBOSE_PREFIX_3 "Registered indication country '%s'\n",zone->country);
345 /* remove an existing country and all its indications, country must exist.
346 * Also, all countries which are an alias for the specified country are removed. */
347 int ast_unregister_indication_country(const char *country)
349 struct tone_zone *tz, *pz = NULL, *tmp;
352 if (PTHREAD_MUTEX_LOCK(&tzlock)) {
353 ast_log(LOG_WARNING, "Unable to lock tone_zones list\n");
359 (strcasecmp(country, tz->country)==0 ||
360 strcasecmp(country, tz->alias)==0)) {
361 /* tone_zone found, remove */
367 /* if we are unregistering the default country, w'll notice */
368 if (tz == current_tonezone) {
369 ast_log(LOG_NOTICE,"Removed default indication country '%s'\n",tz->country);
370 current_tonezone = NULL;
372 ast_verbose(VERBOSE_PREFIX_3 "Unregistered indication country '%s'\n",tz->country);
378 /* next zone please */
383 PTHREAD_MUTEX_UNLOCK(&tzlock);
387 /* add a new indication to a tone_zone. tone_zone must exist. if the indication already
388 * exists, it will be replaced. */
389 int ast_register_indication(struct tone_zone *zone, const char *indication, const char *tonelist)
391 struct tone_zone_sound *ts,*ps;
393 /* is it an alias? stop */
397 if (PTHREAD_MUTEX_LOCK(&tzlock)) {
398 ast_log(LOG_WARNING, "Unable to lock tone_zones list\n");
401 for (ps=NULL,ts=zone->tones; ts; ps=ts,ts=ts->next) {
402 if (strcasecmp(indication,ts->name)==0) {
403 /* indication already there, replace */
404 free((void*)ts->name);
405 free((void*)ts->data);
410 /* not there, we have to add */
411 ts = malloc(sizeof(struct tone_zone_sound));
413 ast_log(LOG_WARNING, "Out of memory\n");
414 PTHREAD_MUTEX_UNLOCK(&tzlock);
419 ts->name = strdup(indication);
420 ts->data = strdup(tonelist);
421 if (ts->name==NULL || ts->data==NULL) {
422 ast_log(LOG_WARNING, "Out of memory\n");
423 PTHREAD_MUTEX_UNLOCK(&tzlock);
430 PTHREAD_MUTEX_UNLOCK(&tzlock);
434 /* remove an existing country's indication. Both country and indication must exist */
435 int ast_unregister_indication(struct tone_zone *zone, const char *indication)
437 struct tone_zone_sound *ts,*ps = NULL, *tmp;
440 /* is it an alias? stop */
444 if (PTHREAD_MUTEX_LOCK(&tzlock)) {
445 ast_log(LOG_WARNING, "Unable to lock tone_zones list\n");
450 if (strcasecmp(indication,ts->name)==0) {
451 /* indication found */
457 free((void*)ts->name);
458 free((void*)ts->data);
464 /* next zone please */
469 /* indication not found, goodbye */
470 PTHREAD_MUTEX_UNLOCK(&tzlock);