Wed Oct 28 11:51:04 2009

Asterisk developer's documentation


indications.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2002, Pauline Middelink
00005  *
00006  *
00007  * See http://www.asterisk.org for more information about
00008  * the Asterisk project. Please do not directly contact
00009  * any of the maintainers of this project for assistance;
00010  * the project provides a web site, mailing lists and IRC
00011  * channels for your use.
00012  *
00013  * This program is free software, distributed under the terms of
00014  * the GNU General Public License Version 2. See the LICENSE file
00015  * at the top of the source tree.
00016  */
00017 
00018 /*! \file
00019  *
00020  * \brief Tone Management
00021  * 
00022  * \author Pauline Middelink <middelink@polyware.nl>
00023  *
00024  * This set of function allow us to play a list of tones on a channel.
00025  * Each element has two frequencies, which are mixed together and a
00026  * duration. For silence both frequencies can be set to 0.
00027  * The playtones can be given as a comma separated string.
00028  *
00029  */
00030 
00031 #include "asterisk.h"
00032 
00033 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 211569 $")
00034 
00035 #include <math.h>
00036 
00037 #include "asterisk/lock.h"
00038 #include "asterisk/linkedlists.h"
00039 #include "asterisk/indications.h"
00040 #include "asterisk/frame.h"
00041 #include "asterisk/channel.h"
00042 #include "asterisk/utils.h"
00043 
00044 static int midi_tohz[128] = {
00045          8,8,9,9,10,10,11,12,12,13,14,
00046          15,16,17,18,19,20,21,23,24,25,
00047          27,29,30,32,34,36,38,41,43,46,
00048          48,51,55,58,61,65,69,73,77,82,
00049          87,92,97,103,110,116,123,130,138,146,
00050          155,164,174,184,195,207,220,233,246,261,
00051          277,293,311,329,349,369,391,415,440,466,
00052          493,523,554,587,622,659,698,739,783,830,
00053          880,932,987,1046,1108,1174,1244,1318,1396,1479,
00054          1567,1661,1760,1864,1975,2093,2217,2349,2489,2637,
00055          2793,2959,3135,3322,3520,3729,3951,4186,4434,4698,
00056          4978,5274,5587,5919,6271,6644,7040,7458,7902,8372,
00057          8869,9397,9956,10548,11175,11839,12543
00058          };
00059 
00060 struct playtones_item {
00061    int fac1;
00062    int init_v2_1;
00063    int init_v3_1;
00064    int fac2;
00065    int init_v2_2;
00066    int init_v3_2;
00067    int modulate;
00068    int duration;
00069 };
00070 
00071 struct playtones_def {
00072    int vol;
00073    int reppos;
00074    int nitems;
00075    int interruptible;
00076    struct playtones_item *items;
00077 };
00078 
00079 struct playtones_state {
00080    int vol;
00081    int v1_1;
00082    int v2_1;
00083    int v3_1;
00084    int v1_2;
00085    int v2_2;
00086    int v3_2;
00087    int reppos;
00088    int nitems;
00089    struct playtones_item *items;
00090    int npos;
00091    int oldnpos;
00092    int pos;
00093    int origwfmt;
00094    struct ast_frame f;
00095    unsigned char offset[AST_FRIENDLY_OFFSET];
00096    short data[4000];
00097 };
00098 
00099 static void playtones_release(struct ast_channel *chan, void *params)
00100 {
00101    struct playtones_state *ps = params;
00102 
00103    if (chan)
00104       ast_set_write_format(chan, ps->origwfmt);
00105    if (ps->items)
00106       ast_free(ps->items);
00107 
00108    ast_free(ps);
00109 }
00110 
00111 static void * playtones_alloc(struct ast_channel *chan, void *params)
00112 {
00113    struct playtones_def *pd = params;
00114    struct playtones_state *ps = NULL;
00115 
00116    if (!(ps = ast_calloc(1, sizeof(*ps))))
00117       return NULL;
00118 
00119    ps->origwfmt = chan->writeformat;
00120 
00121    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
00122       ast_log(LOG_WARNING, "Unable to set '%s' to signed linear format (write)\n", chan->name);
00123       playtones_release(NULL, ps);
00124       ps = NULL;
00125    } else {
00126       ps->vol = pd->vol;
00127       ps->reppos = pd->reppos;
00128       ps->nitems = pd->nitems;
00129       ps->items = pd->items;
00130       ps->oldnpos = -1;
00131    }
00132 
00133    /* Let interrupts interrupt :) */
00134    if (pd->interruptible)
00135       ast_set_flag(chan, AST_FLAG_WRITE_INT);
00136    else
00137       ast_clear_flag(chan, AST_FLAG_WRITE_INT);
00138 
00139    return ps;
00140 }
00141 
00142 static int playtones_generator(struct ast_channel *chan, void *data, int len, int samples)
00143 {
00144    struct playtones_state *ps = data;
00145    struct playtones_item *pi;
00146    int x;
00147    /* we need to prepare a frame with 16 * timelen samples as we're 
00148     * generating SLIN audio
00149     */
00150    len = samples * 2;
00151    if (len > sizeof(ps->data) / 2 - 1) {
00152       ast_log(LOG_WARNING, "Can't generate that much data!\n");
00153       return -1;
00154    }
00155    memset(&ps->f, 0, sizeof(ps->f));
00156 
00157    pi = &ps->items[ps->npos];
00158    if (ps->oldnpos != ps->npos) {
00159       /* Load new parameters */
00160       ps->v1_1 = 0;
00161       ps->v2_1 = pi->init_v2_1;
00162       ps->v3_1 = pi->init_v3_1;
00163       ps->v1_2 = 0;
00164       ps->v2_2 = pi->init_v2_2;
00165       ps->v3_2 = pi->init_v3_2;
00166       ps->oldnpos = ps->npos;
00167    }
00168    for (x = 0; x < len/2; x++) {
00169       ps->v1_1 = ps->v2_1;
00170       ps->v2_1 = ps->v3_1;
00171       ps->v3_1 = (pi->fac1 * ps->v2_1 >> 15) - ps->v1_1;
00172       
00173       ps->v1_2 = ps->v2_2;
00174       ps->v2_2 = ps->v3_2;
00175       ps->v3_2 = (pi->fac2 * ps->v2_2 >> 15) - ps->v1_2;
00176       if (pi->modulate) {
00177          int p;
00178          p = ps->v3_2 - 32768;
00179          if (p < 0) p = -p;
00180          p = ((p * 9) / 10) + 1;
00181          ps->data[x] = (ps->v3_1 * p) >> 15;
00182       } else
00183          ps->data[x] = ps->v3_1 + ps->v3_2; 
00184    }
00185    
00186    ps->f.frametype = AST_FRAME_VOICE;
00187    ps->f.subclass = AST_FORMAT_SLINEAR;
00188    ps->f.datalen = len;
00189    ps->f.samples = samples;
00190    ps->f.offset = AST_FRIENDLY_OFFSET;
00191    ps->f.data.ptr = ps->data;
00192    ps->f.delivery.tv_sec = 0;
00193    ps->f.delivery.tv_usec = 0;
00194    ast_write(chan, &ps->f);
00195 
00196    ps->pos += x;
00197    if (pi->duration && ps->pos >= pi->duration * 8) { /* item finished? */
00198       ps->pos = 0;               /* start new item */
00199       ps->npos++;
00200       if (ps->npos >= ps->nitems) {       /* last item? */
00201          if (ps->reppos == -1)         /* repeat set? */
00202             return -1;
00203          ps->npos = ps->reppos;        /* redo from top */
00204       }
00205    }
00206    return 0;
00207 }
00208 
00209 static struct ast_generator playtones = {
00210    alloc: playtones_alloc,
00211    release: playtones_release,
00212    generate: playtones_generator,
00213 };
00214 
00215 int ast_playtones_start(struct ast_channel *chan, int vol, const char *playlst, int interruptible)
00216 {
00217    char *s, *data = ast_strdupa(playlst); /* cute */
00218    struct playtones_def d = { vol, -1, 0, 1, NULL};
00219    char *stringp;
00220    char *separator;
00221    
00222    if (vol < 1)
00223       d.vol = 7219; /* Default to -8db */
00224 
00225    d.interruptible = interruptible;
00226    
00227    stringp=data;
00228    /* the stringp/data is not null here */
00229    /* check if the data is separated with '|' or with ',' by default */
00230    if (strchr(stringp,'|'))
00231       separator = "|";
00232    else
00233       separator = ",";
00234    s = strsep(&stringp,separator);
00235    while (s && *s) {
00236       int freq1, freq2, duration, modulate = 0, midinote = 0;
00237 
00238       if (s[0]=='!')
00239          s++;
00240       else if (d.reppos == -1)
00241          d.reppos = d.nitems;
00242       if (sscanf(s, "%30d+%30d/%30d", &freq1, &freq2, &duration) == 3) {
00243          /* f1+f2/time format */
00244       } else if (sscanf(s, "%30d+%30d", &freq1, &freq2) == 2) {
00245          /* f1+f2 format */
00246          duration = 0;
00247       } else if (sscanf(s, "%30d*%30d/%30d", &freq1, &freq2, &duration) == 3) {
00248          /* f1*f2/time format */
00249          modulate = 1;
00250       } else if (sscanf(s, "%30d*%30d", &freq1, &freq2) == 2) {
00251          /* f1*f2 format */
00252          duration = 0;
00253          modulate = 1;
00254       } else if (sscanf(s, "%30d/%30d", &freq1, &duration) == 2) {
00255          /* f1/time format */
00256          freq2 = 0;
00257       } else if (sscanf(s, "%30d", &freq1) == 1) {
00258          /* f1 format */
00259          freq2 = 0;
00260          duration = 0;
00261       } else if (sscanf(s, "M%30d+M%30d/%30d", &freq1, &freq2, &duration) == 3) {
00262          /* Mf1+Mf2/time format */
00263          midinote = 1;
00264       } else if (sscanf(s, "M%30d+M%30d", &freq1, &freq2) == 2) {
00265          /* Mf1+Mf2 format */
00266          duration = 0;
00267          midinote = 1;
00268       } else if (sscanf(s, "M%30d*M%30d/%30d", &freq1, &freq2, &duration) == 3) {
00269          /* Mf1*Mf2/time format */
00270          modulate = 1;
00271          midinote = 1;
00272       } else if (sscanf(s, "M%30d*M%30d", &freq1, &freq2) == 2) {
00273          /* Mf1*Mf2 format */
00274          duration = 0;
00275          modulate = 1;
00276          midinote = 1;
00277       } else if (sscanf(s, "M%30d/%30d", &freq1, &duration) == 2) {
00278          /* Mf1/time format */
00279          freq2 = -1;
00280          midinote = 1;
00281       } else if (sscanf(s, "M%30d", &freq1) == 1) {
00282          /* Mf1 format */
00283          freq2 = -1;
00284          duration = 0;
00285          midinote = 1;
00286       } else {
00287          ast_log(LOG_WARNING,"%s: tone component '%s' of '%s' is no good\n",chan->name,s,playlst);
00288          return -1;
00289       }
00290 
00291       if (midinote) {
00292          /* midi notes must be between 0 and 127 */
00293          if ((freq1 >= 0) && (freq1 <= 127))
00294             freq1 = midi_tohz[freq1];
00295          else
00296             freq1 = 0;
00297 
00298          if ((freq2 >= 0) && (freq2 <= 127))
00299             freq2 = midi_tohz[freq2];
00300          else
00301             freq2 = 0;
00302       }
00303 
00304       if (!(d.items = ast_realloc(d.items, (d.nitems + 1) * sizeof(*d.items)))) {
00305          return -1;
00306       }
00307       d.items[d.nitems].fac1 = 2.0 * cos(2.0 * M_PI * (freq1 / 8000.0)) * 32768.0;
00308       d.items[d.nitems].init_v2_1 = sin(-4.0 * M_PI * (freq1 / 8000.0)) * d.vol;
00309       d.items[d.nitems].init_v3_1 = sin(-2.0 * M_PI * (freq1 / 8000.0)) * d.vol;
00310 
00311       d.items[d.nitems].fac2 = 2.0 * cos(2.0 * M_PI * (freq2 / 8000.0)) * 32768.0;
00312       d.items[d.nitems].init_v2_2 = sin(-4.0 * M_PI * (freq2 / 8000.0)) * d.vol;
00313       d.items[d.nitems].init_v3_2 = sin(-2.0 * M_PI * (freq2 / 8000.0)) * d.vol;
00314       d.items[d.nitems].duration = duration;
00315       d.items[d.nitems].modulate = modulate;
00316       d.nitems++;
00317 
00318       s = strsep(&stringp,separator);
00319    }
00320 
00321    if (ast_activate_generator(chan, &playtones, &d)) {
00322       ast_free(d.items);
00323       return -1;
00324    }
00325    return 0;
00326 }
00327 
00328 void ast_playtones_stop(struct ast_channel *chan)
00329 {
00330    ast_deactivate_generator(chan);
00331 }
00332 
00333 /*--------------------------------------------*/
00334 
00335 static AST_RWLIST_HEAD_STATIC(tone_zones, tone_zone);
00336 static struct tone_zone *current_tonezone;
00337 
00338 struct tone_zone *ast_walk_indications(const struct tone_zone *cur)
00339 {
00340    struct tone_zone *tz = NULL;
00341 
00342    AST_RWLIST_RDLOCK(&tone_zones);
00343    /* If cur is not NULL, then we have to iterate through - otherwise just return the first entry */
00344    if (cur) {
00345       AST_RWLIST_TRAVERSE(&tone_zones, tz, list) {
00346          if (tz == cur)
00347             break;
00348       }
00349       tz = AST_RWLIST_NEXT(tz, list);
00350    } else {
00351       tz = AST_RWLIST_FIRST(&tone_zones);
00352    }
00353    AST_RWLIST_UNLOCK(&tone_zones);
00354 
00355    return tz;
00356 }
00357 
00358 /* Set global indication country */
00359 int ast_set_indication_country(const char *country)
00360 {
00361    struct tone_zone *zone = NULL;
00362 
00363    /* If no country is specified or we are unable to find the zone, then return not found */
00364    if (!country || !(zone = ast_get_indication_zone(country)))
00365       return 1;
00366    
00367    ast_verb(3, "Setting default indication country to '%s'\n", country);
00368 
00369    /* Protect the current tonezone using the tone_zones lock as well */
00370    AST_RWLIST_WRLOCK(&tone_zones);
00371    current_tonezone = zone;
00372    AST_RWLIST_UNLOCK(&tone_zones);
00373 
00374    /* Zone was found */
00375    return 0;
00376 }
00377 
00378 /* locate tone_zone, given the country. if country == NULL, use the default country */
00379 struct tone_zone *ast_get_indication_zone(const char *country)
00380 {
00381    struct tone_zone *tz = NULL;
00382    int alias_loop = 0;
00383 
00384    AST_RWLIST_RDLOCK(&tone_zones);
00385 
00386    if (ast_strlen_zero(country)) {
00387       tz = current_tonezone ? current_tonezone : AST_LIST_FIRST(&tone_zones);
00388    } else {
00389       do {
00390          AST_RWLIST_TRAVERSE(&tone_zones, tz, list) {
00391             if (!strcasecmp(tz->country, country))
00392                break;
00393          }
00394          if (!tz)
00395             break;
00396          /* If this is an alias then we have to search yet again otherwise we have found the zonezone */
00397          if (tz->alias && tz->alias[0])
00398             country = tz->alias;
00399          else
00400             break;
00401       } while ((++alias_loop < 20) && tz);
00402    }
00403 
00404    AST_RWLIST_UNLOCK(&tone_zones);
00405 
00406    /* If we reached the maximum loops to find the proper country via alias, print out a notice */
00407    if (alias_loop == 20)
00408       ast_log(LOG_NOTICE, "Alias loop for '%s' is bonkers\n", country);
00409 
00410    return tz;
00411 }
00412 
00413 /* locate a tone_zone_sound, given the tone_zone. if tone_zone == NULL, use the default tone_zone */
00414 struct tone_zone_sound *ast_get_indication_tone(const struct tone_zone *zone, const char *indication)
00415 {
00416    struct tone_zone_sound *ts = NULL;
00417 
00418    AST_RWLIST_RDLOCK(&tone_zones);
00419 
00420    /* If no zone is already specified we need to try to pick one */
00421    if (!zone) {
00422       if (current_tonezone) {
00423          zone = current_tonezone;
00424       } else if (!(zone = AST_LIST_FIRST(&tone_zones))) {
00425          /* No zone has been found ;( */
00426          AST_RWLIST_UNLOCK(&tone_zones);
00427          return NULL;
00428       }
00429    }
00430 
00431    /* Look through list of tones in the zone searching for the right one */
00432    for (ts = zone->tones; ts; ts = ts->next) {
00433       if (!strcasecmp(ts->name, indication))
00434          break;
00435    }
00436 
00437    AST_RWLIST_UNLOCK(&tone_zones);
00438 
00439    return ts;
00440 }
00441 
00442 /* helper function to delete a tone_zone in its entirety */
00443 static inline void free_zone(struct tone_zone* zone)
00444 {
00445    while (zone->tones) {
00446       struct tone_zone_sound *tmp = zone->tones->next;
00447       ast_free((void *)zone->tones->name);
00448       ast_free((void *)zone->tones->data);
00449       ast_free(zone->tones);
00450       zone->tones = tmp;
00451    }
00452 
00453    if (zone->ringcadence)
00454       ast_free(zone->ringcadence);
00455 
00456    ast_free(zone);
00457 }
00458 
00459 /*--------------------------------------------*/
00460 
00461 /* add a new country, if country exists, it will be replaced. */
00462 int ast_register_indication_country(struct tone_zone *zone)
00463 {
00464    struct tone_zone *tz = NULL;
00465 
00466    AST_RWLIST_WRLOCK(&tone_zones);
00467    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&tone_zones, tz, list) {
00468       /* If this is not the same zone, then just continue to the next entry */
00469       if (strcasecmp(zone->country, tz->country))
00470          continue;
00471       /* If this zone we are going to remove is the current default then make the new zone the default */
00472       if (tz == current_tonezone)
00473          current_tonezone = zone;
00474       /* Remove from the linked list */
00475       AST_RWLIST_REMOVE_CURRENT(list);
00476       /* Finally free the zone itself */
00477       free_zone(tz);
00478       break;
00479    }
00480    AST_RWLIST_TRAVERSE_SAFE_END;
00481 
00482    /* Add zone to the list */
00483    AST_RWLIST_INSERT_TAIL(&tone_zones, zone, list);
00484 
00485    /* It's all over. */
00486    AST_RWLIST_UNLOCK(&tone_zones);
00487 
00488    ast_verb(3, "Registered indication country '%s'\n", zone->country);
00489 
00490    return 0;
00491 }
00492 
00493 /* remove an existing country and all its indications, country must exist.
00494  * Also, all countries which are an alias for the specified country are removed. */
00495 int ast_unregister_indication_country(const char *country)
00496 {
00497    struct tone_zone *tz = NULL;
00498    int res = -1;
00499 
00500    AST_RWLIST_WRLOCK(&tone_zones);
00501    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&tone_zones, tz, list) {
00502       if (country && (strcasecmp(country, tz->country) && strcasecmp(country, tz->alias)))
00503          continue;
00504       /* If this tonezone is the current default then unset it */
00505       if (tz == current_tonezone) {
00506          ast_log(LOG_NOTICE,"Removed default indication country '%s'\n", tz->country);
00507          current_tonezone = NULL;
00508       }
00509       /* Remove from the list */
00510       AST_RWLIST_REMOVE_CURRENT(list);
00511       ast_verb(3, "Unregistered indication country '%s'\n", tz->country);
00512       free_zone(tz);
00513       res = 0;
00514    }
00515    AST_RWLIST_TRAVERSE_SAFE_END;
00516    AST_RWLIST_UNLOCK(&tone_zones);
00517 
00518    return res;
00519 }
00520 
00521 /* add a new indication to a tone_zone. tone_zone must exist. if the indication already
00522  * exists, it will be replaced. */
00523 int ast_register_indication(struct tone_zone *zone, const char *indication, const char *tonelist)
00524 {
00525    struct tone_zone_sound *ts, *ps;
00526 
00527    /* is it an alias? stop */
00528    if (zone->alias[0])
00529       return -1;
00530 
00531    AST_RWLIST_WRLOCK(&tone_zones);
00532    for (ps=NULL,ts=zone->tones; ts; ps=ts,ts=ts->next) {
00533       if (!strcasecmp(indication,ts->name)) {
00534          /* indication already there, replace */
00535          ast_free((void*)ts->name);
00536          ast_free((void*)ts->data);
00537          break;
00538       }
00539    }
00540    if (!ts) {
00541       /* not there, we have to add */
00542       if (!(ts = ast_malloc(sizeof(*ts)))) {
00543          AST_RWLIST_UNLOCK(&tone_zones);
00544          return -2;
00545       }
00546       ts->next = NULL;
00547    }
00548    if (!(ts->name = ast_strdup(indication)) || !(ts->data = ast_strdup(tonelist))) {
00549       AST_RWLIST_UNLOCK(&tone_zones);
00550       return -2;
00551    }
00552    if (ps)
00553       ps->next = ts;
00554    else
00555       zone->tones = ts;
00556    AST_RWLIST_UNLOCK(&tone_zones);
00557    return 0;
00558 }
00559 
00560 /* remove an existing country's indication. Both country and indication must exist */
00561 int ast_unregister_indication(struct tone_zone *zone, const char *indication)
00562 {
00563    struct tone_zone_sound *ts,*ps = NULL, *tmp;
00564    int res = -1;
00565 
00566    /* is it an alias? stop */
00567    if (zone->alias[0])
00568       return -1;
00569 
00570    AST_RWLIST_WRLOCK(&tone_zones);
00571    ts = zone->tones;
00572    while (ts) {
00573       if (!strcasecmp(indication,ts->name)) {
00574          /* indication found */
00575          tmp = ts->next;
00576          if (ps)
00577             ps->next = tmp;
00578          else
00579             zone->tones = tmp;
00580          ast_free((void*)ts->name);
00581          ast_free((void*)ts->data);
00582          ast_free(ts);
00583          ts = tmp;
00584          res = 0;
00585       }
00586       else {
00587          /* next zone please */
00588          ps = ts;
00589          ts = ts->next;
00590       }
00591    }
00592    /* indication not found, goodbye */
00593    AST_RWLIST_UNLOCK(&tone_zones);
00594    return res;
00595 }

Generated on Wed Oct 28 11:51:04 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6