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