Wed Apr 6 11:29:49 2011

Asterisk developer's documentation


app_festival.c File Reference

Connect to festival. More...

#include "asterisk.h"
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <errno.h>
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/md5.h"
#include "asterisk/config.h"
#include "asterisk/utils.h"
#include "asterisk/lock.h"
#include "asterisk/app.h"
#include "asterisk/endian.h"

Go to the source code of this file.

Defines

#define FESTIVAL_CONFIG   "festival.conf"
#define MAXFESTLEN   2048
#define MAXLEN   180

Functions

static void __reg_module (void)
static void __unreg_module (void)
static int festival_exec (struct ast_channel *chan, const char *vdata)
static int load_module (void)
static int send_waveform_to_channel (struct ast_channel *chan, char *waveform, int length, char *intkeys)
static int send_waveform_to_fd (char *waveform, int length, int fd)
static char * socket_receive_file_to_buff (int fd, int *size)
static int unload_module (void)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Simple Festival Interface" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "8586c2a7d357cb591cc3a6607a8f62d1" , .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, }
static char * app = "Festival"
static struct ast_module_infoast_module_info = &__mod_info


Detailed Description

Connect to festival.

Author:
Christos Ricudis <ricudis@itc.auth.gr>
ExtRef:
The Festival Speech Synthesis System - http://www.cstr.ed.ac.uk/projects/festival/

Definition in file app_festival.c.


Define Documentation

#define FESTIVAL_CONFIG   "festival.conf"

Definition at line 54 of file app_festival.c.

Referenced by festival_exec(), and load_module().

#define MAXFESTLEN   2048

Definition at line 56 of file app_festival.c.

Referenced by festival_exec().

#define MAXLEN   180

Definition at line 55 of file app_festival.c.


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 558 of file app_festival.c.

static void __unreg_module ( void   )  [static]

Definition at line 558 of file app_festival.c.

static int festival_exec ( struct ast_channel chan,
const char *  vdata 
) [static]

Definition at line 269 of file app_festival.c.

References ahp, args, AST_APP_ARG, ast_config_destroy(), ast_config_load, ast_debug, AST_DECLARE_APP_ARGS, AST_DIGIT_ANY, AST_FILE_MODE, ast_free, ast_gethostbyname(), ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_true(), ast_variable_retrieve(), config_flags, CONFIG_STATUS_FILEINVALID, errno, FESTIVAL_CONFIG, LOG_ERROR, LOG_WARNING, MAXFESTLEN, MD5Final(), MD5Init(), MD5Update(), send_waveform_to_channel(), socket_receive_file_to_buff(), text, and wave.

Referenced by load_module().

00270 {
00271    int usecache;
00272    int res = 0;
00273    struct sockaddr_in serv_addr;
00274    struct hostent *serverhost;
00275    struct ast_hostent ahp;
00276    int fd;
00277    FILE *fs;
00278    const char *host;
00279    const char *cachedir;
00280    const char *temp;
00281    const char *festivalcommand;
00282    int port = 1314;
00283    int n;
00284    char ack[4];
00285    char *waveform;
00286    int filesize;
00287    int wave;
00288    char bigstring[MAXFESTLEN];
00289    int i;
00290    struct MD5Context md5ctx;
00291    unsigned char MD5Res[16];
00292    char MD5Hex[33] = "";
00293    char koko[4] = "";
00294    char cachefile[MAXFESTLEN]="";
00295    int readcache = 0;
00296    int writecache = 0;
00297    int strln;
00298    int fdesc = -1;
00299    char buffer[16384];
00300    int seekpos = 0;  
00301    char *data; 
00302    struct ast_config *cfg;
00303    char *newfestivalcommand;
00304    struct ast_flags config_flags = { 0 };
00305    AST_DECLARE_APP_ARGS(args,
00306       AST_APP_ARG(text);
00307       AST_APP_ARG(interrupt);
00308    );
00309 
00310    if (ast_strlen_zero(vdata)) {
00311       ast_log(LOG_WARNING, "festival requires an argument (text)\n");
00312       return -1;
00313    }
00314 
00315    cfg = ast_config_load(FESTIVAL_CONFIG, config_flags);
00316    if (!cfg) {
00317       ast_log(LOG_WARNING, "No such configuration file %s\n", FESTIVAL_CONFIG);
00318       return -1;
00319    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
00320       ast_log(LOG_ERROR, "Config file " FESTIVAL_CONFIG " is in an invalid format.  Aborting.\n");
00321       return -1;
00322    }
00323 
00324    if (!(host = ast_variable_retrieve(cfg, "general", "host"))) {
00325       host = "localhost";
00326    }
00327    if (!(temp = ast_variable_retrieve(cfg, "general", "port"))) {
00328       port = 1314;
00329    } else {
00330       port = atoi(temp);
00331    }
00332    if (!(temp = ast_variable_retrieve(cfg, "general", "usecache"))) {
00333       usecache = 0;
00334    } else {
00335       usecache = ast_true(temp);
00336    }
00337    if (!(cachedir = ast_variable_retrieve(cfg, "general", "cachedir"))) {
00338       cachedir = "/tmp/";
00339    }
00340 
00341    data = ast_strdupa(vdata);
00342    AST_STANDARD_APP_ARGS(args, data);
00343 
00344    if (!(festivalcommand = ast_variable_retrieve(cfg, "general", "festivalcommand"))) {
00345       const char *startcmd = "(tts_textasterisk \"";
00346       const char *endcmd = "\" 'file)(quit)\n";
00347 
00348       strln = strlen(startcmd) + strlen(args.text) + strlen(endcmd) + 1;
00349       newfestivalcommand = alloca(strln);
00350       snprintf(newfestivalcommand, strln, "%s%s%s", startcmd, args.text, endcmd);
00351       festivalcommand = newfestivalcommand;
00352    } else { /* This else parses the festivalcommand that we're sent from the config file for \n's, etc */
00353       int x, j;
00354       newfestivalcommand = alloca(strlen(festivalcommand) + strlen(args.text) + 1);
00355 
00356       for (x = 0, j = 0; x < strlen(festivalcommand); x++) {
00357          if (festivalcommand[x] == '\\' && festivalcommand[x + 1] == 'n') {
00358             newfestivalcommand[j++] = '\n';
00359             x++;
00360          } else if (festivalcommand[x] == '\\') {
00361             newfestivalcommand[j++] = festivalcommand[x + 1];
00362             x++;
00363          } else if (festivalcommand[x] == '%' && festivalcommand[x + 1] == 's') {
00364             sprintf(&newfestivalcommand[j], "%s", args.text); /* we know it is big enough */
00365             j += strlen(args.text);
00366             x++;
00367          } else
00368             newfestivalcommand[j++] = festivalcommand[x];
00369       }
00370       newfestivalcommand[j] = '\0';
00371       festivalcommand = newfestivalcommand;
00372    }
00373    
00374    if (args.interrupt && !strcasecmp(args.interrupt, "any"))
00375       args.interrupt = AST_DIGIT_ANY;
00376 
00377    ast_debug(1, "Text passed to festival server : %s\n", args.text);
00378    /* Connect to local festival server */
00379    
00380    fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
00381 
00382    if (fd < 0) {
00383       ast_log(LOG_WARNING, "festival_client: can't get socket\n");
00384       ast_config_destroy(cfg);
00385       return -1;
00386    }
00387 
00388    memset(&serv_addr, 0, sizeof(serv_addr));
00389 
00390    if ((serv_addr.sin_addr.s_addr = inet_addr(host)) == -1) {
00391       /* its a name rather than an ipnum */
00392       serverhost = ast_gethostbyname(host, &ahp);
00393 
00394       if (serverhost == NULL) {
00395          ast_log(LOG_WARNING, "festival_client: gethostbyname failed\n");
00396          ast_config_destroy(cfg);
00397          return -1;
00398       }
00399       memmove(&serv_addr.sin_addr, serverhost->h_addr, serverhost->h_length);
00400    }
00401 
00402    serv_addr.sin_family = AF_INET;
00403    serv_addr.sin_port = htons(port);
00404 
00405    if (connect(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) != 0) {
00406       ast_log(LOG_WARNING, "festival_client: connect to server failed\n");
00407       ast_config_destroy(cfg);
00408       return -1;
00409    }
00410 
00411    /* Compute MD5 sum of string */
00412    MD5Init(&md5ctx);
00413    MD5Update(&md5ctx, (unsigned char *)args.text, strlen(args.text));
00414    MD5Final(MD5Res, &md5ctx);
00415    MD5Hex[0] = '\0';
00416 
00417    /* Convert to HEX and look if there is any matching file in the cache 
00418       directory */
00419    for (i = 0; i < 16; i++) {
00420       snprintf(koko, sizeof(koko), "%X", MD5Res[i]);
00421       strncat(MD5Hex, koko, sizeof(MD5Hex) - strlen(MD5Hex) - 1);
00422    }
00423    readcache = 0;
00424    writecache = 0;
00425    if (strlen(cachedir) + strlen(MD5Hex) + 1 <= MAXFESTLEN && (usecache == -1)) {
00426       snprintf(cachefile, sizeof(cachefile), "%s/%s", cachedir, MD5Hex);
00427       fdesc = open(cachefile, O_RDWR);
00428       if (fdesc == -1) {
00429          fdesc = open(cachefile, O_CREAT | O_RDWR, AST_FILE_MODE);
00430          if (fdesc != -1) {
00431             writecache = 1;
00432             strln = strlen(args.text);
00433             ast_debug(1, "line length : %d\n", strln);
00434                if (write(fdesc,&strln,sizeof(int)) < 0) {
00435                ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
00436             }
00437                if (write(fdesc,data,strln) < 0) {
00438                ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
00439             }
00440             seekpos = lseek(fdesc, 0, SEEK_CUR);
00441             ast_debug(1, "Seek position : %d\n", seekpos);
00442          }
00443       } else {
00444             if (read(fdesc,&strln,sizeof(int)) != sizeof(int)) {
00445             ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
00446          }
00447          ast_debug(1, "Cache file exists, strln=%d, strlen=%d\n", strln, (int)strlen(args.text));
00448          if (strlen(args.text) == strln) {
00449             ast_debug(1, "Size OK\n");
00450                if (read(fdesc,&bigstring,strln) != strln) {
00451                ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
00452             }
00453             bigstring[strln] = 0;
00454             if (strcmp(bigstring, args.text) == 0) { 
00455                readcache = 1;
00456             } else {
00457                ast_log(LOG_WARNING, "Strings do not match\n");
00458             }
00459          } else {
00460             ast_log(LOG_WARNING, "Size mismatch\n");
00461          }
00462       }
00463    }
00464 
00465    if (readcache == 1) {
00466       close(fd);
00467       fd = fdesc;
00468       ast_debug(1, "Reading from cache...\n");
00469    } else {
00470       ast_debug(1, "Passing text to festival...\n");
00471       fs = fdopen(dup(fd), "wb");
00472 
00473       fprintf(fs, "%s", festivalcommand);
00474       fflush(fs);
00475       fclose(fs);
00476    }
00477    
00478    /* Write to cache and then pass it down */
00479    if (writecache == 1) {
00480       ast_debug(1, "Writing result to cache...\n");
00481       while ((strln = read(fd, buffer, 16384)) != 0) {
00482          if (write(fdesc,buffer,strln) < 0) {
00483             ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
00484          }
00485       }
00486       close(fd);
00487       close(fdesc);
00488       fd = open(cachefile, O_RDWR);
00489       lseek(fd, seekpos, SEEK_SET);
00490    }
00491    
00492    ast_debug(1, "Passing data to channel...\n");
00493 
00494    /* Read back info from server */
00495    /* This assumes only one waveform will come back, also LP is unlikely */
00496    wave = 0;
00497    do {
00498       int read_data;
00499       for (n = 0; n < 3; ) {
00500          read_data = read(fd, ack + n, 3 - n);
00501          /* this avoids falling in infinite loop
00502           * in case that festival server goes down
00503           */
00504          if (read_data == -1) {
00505             ast_log(LOG_WARNING, "Unable to read from cache/festival fd\n");
00506             close(fd);
00507             ast_config_destroy(cfg);
00508             return -1;
00509          }
00510          n += read_data;
00511       }
00512       ack[3] = '\0';
00513       if (strcmp(ack, "WV\n") == 0) {         /* receive a waveform */
00514          ast_debug(1, "Festival WV command\n");
00515          if ((waveform = socket_receive_file_to_buff(fd, &filesize))) {
00516             res = send_waveform_to_channel(chan, waveform, filesize, args.interrupt);
00517             ast_free(waveform);
00518          }
00519          break;
00520       } else if (strcmp(ack, "LP\n") == 0) {   /* receive an s-expr */
00521          ast_debug(1, "Festival LP command\n");
00522          if ((waveform = socket_receive_file_to_buff(fd, &filesize))) {
00523             waveform[filesize] = '\0';
00524             ast_log(LOG_WARNING, "Festival returned LP : %s\n", waveform);
00525             ast_free(waveform);
00526          }
00527       } else if (strcmp(ack, "ER\n") == 0) {    /* server got an error */
00528          ast_log(LOG_WARNING, "Festival returned ER\n");
00529          res = -1;
00530          break;
00531       }
00532    } while (strcmp(ack, "OK\n") != 0);
00533    close(fd);
00534    ast_config_destroy(cfg);
00535    return res;
00536 }

static int load_module ( void   )  [static]

Definition at line 543 of file app_festival.c.

References ast_config_destroy(), ast_config_load, ast_log(), AST_MODULE_LOAD_DECLINE, ast_register_application_xml, config_flags, CONFIG_STATUS_FILEINVALID, FESTIVAL_CONFIG, festival_exec(), LOG_ERROR, and LOG_WARNING.

00544 {
00545    struct ast_flags config_flags = { 0 };
00546    struct ast_config *cfg = ast_config_load(FESTIVAL_CONFIG, config_flags);
00547    if (!cfg) {
00548       ast_log(LOG_WARNING, "No such configuration file %s\n", FESTIVAL_CONFIG);
00549       return AST_MODULE_LOAD_DECLINE;
00550    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
00551       ast_log(LOG_ERROR, "Config file " FESTIVAL_CONFIG " is in an invalid format.  Aborting.\n");
00552       return AST_MODULE_LOAD_DECLINE;
00553    }
00554    ast_config_destroy(cfg);
00555    return ast_register_application_xml(app, festival_exec);
00556 }

static int send_waveform_to_channel ( struct ast_channel chan,
char *  waveform,
int  length,
char *  intkeys 
) [static]

Definition at line 162 of file app_festival.c.

References ast_channel::_state, ast_answer(), ast_debug, AST_FORMAT_SLINEAR, AST_FRAME_DTMF, AST_FRAME_VOICE, ast_frfree, AST_FRIENDLY_OFFSET, ast_indicate(), ast_log(), ast_read(), ast_set_write_format(), AST_STATE_UP, ast_stopstream(), ast_waitfor(), ast_write(), f, LOG_WARNING, ast_frame::offset, and send_waveform_to_fd().

Referenced by festival_exec().

00163 {
00164    int res = 0;
00165    int fds[2];
00166    int pid = -1;
00167    int needed = 0;
00168    int owriteformat;
00169    struct ast_frame *f;
00170    struct myframe {
00171       struct ast_frame f;
00172       char offset[AST_FRIENDLY_OFFSET];
00173       char frdata[2048];
00174    } myf = {
00175       .f = { 0, },
00176    };
00177 
00178    if (pipe(fds)) {
00179       ast_log(LOG_WARNING, "Unable to create pipe\n");
00180       return -1;
00181    }
00182 
00183    /* Answer if it's not already going */
00184    if (chan->_state != AST_STATE_UP)
00185       ast_answer(chan);
00186    ast_stopstream(chan);
00187    ast_indicate(chan, -1);
00188    
00189    owriteformat = chan->writeformat;
00190    res = ast_set_write_format(chan, AST_FORMAT_SLINEAR);
00191    if (res < 0) {
00192       ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
00193       return -1;
00194    }
00195    
00196    res = send_waveform_to_fd(waveform, length, fds[1]);
00197    if (res >= 0) {
00198       pid = res;
00199       /* Order is important -- there's almost always going to be mp3...  we want to prioritize the
00200          user */
00201       for (;;) {
00202          res = ast_waitfor(chan, 1000);
00203          if (res < 1) {
00204             res = -1;
00205             break;
00206          }
00207          f = ast_read(chan);
00208          if (!f) {
00209             ast_log(LOG_WARNING, "Null frame == hangup() detected\n");
00210             res = -1;
00211             break;
00212          }
00213          if (f->frametype == AST_FRAME_DTMF) {
00214             ast_debug(1, "User pressed a key\n");
00215             if (intkeys && strchr(intkeys, f->subclass.integer)) {
00216                res = f->subclass.integer;
00217                ast_frfree(f);
00218                break;
00219             }
00220          }
00221          if (f->frametype == AST_FRAME_VOICE) {
00222             /* Treat as a generator */
00223             needed = f->samples * 2;
00224             if (needed > sizeof(myf.frdata)) {
00225                ast_log(LOG_WARNING, "Only able to deliver %d of %d requested samples\n",
00226                   (int)sizeof(myf.frdata) / 2, needed/2);
00227                needed = sizeof(myf.frdata);
00228             }
00229             res = read(fds[0], myf.frdata, needed);
00230             if (res > 0) {
00231                myf.f.frametype = AST_FRAME_VOICE;
00232                myf.f.subclass.codec = AST_FORMAT_SLINEAR;
00233                myf.f.datalen = res;
00234                myf.f.samples = res / 2;
00235                myf.f.offset = AST_FRIENDLY_OFFSET;
00236                myf.f.src = __PRETTY_FUNCTION__;
00237                myf.f.data.ptr = myf.frdata;
00238                if (ast_write(chan, &myf.f) < 0) {
00239                   res = -1;
00240                   ast_frfree(f);
00241                   break;
00242                }
00243                if (res < needed) { /* last frame */
00244                   ast_debug(1, "Last frame\n");
00245                   res = 0;
00246                   ast_frfree(f);
00247                   break;
00248                }
00249             } else {
00250                ast_debug(1, "No more waveform\n");
00251                res = 0;
00252             }
00253          }
00254          ast_frfree(f);
00255       }
00256    }
00257    close(fds[0]);
00258    close(fds[1]);
00259 
00260 #if 0
00261    if (pid > -1)
00262       kill(pid, SIGKILL);
00263 #endif
00264    if (!res && owriteformat)
00265       ast_set_write_format(chan, owriteformat);
00266    return res;
00267 }

static int send_waveform_to_fd ( char *  waveform,
int  length,
int  fd 
) [static]

Definition at line 128 of file app_festival.c.

References ast_close_fds_above_n(), ast_log(), ast_opt_high_priority, ast_safe_fork(), ast_set_priority(), and LOG_WARNING.

Referenced by send_waveform_to_channel().

00129 {
00130    int res;
00131 #if __BYTE_ORDER == __BIG_ENDIAN
00132    int x;
00133    char c;
00134 #endif
00135 
00136    res = ast_safe_fork(0);
00137    if (res < 0)
00138       ast_log(LOG_WARNING, "Fork failed\n");
00139    if (res) {
00140       return res;
00141    }
00142    dup2(fd, 0);
00143    ast_close_fds_above_n(0);
00144    if (ast_opt_high_priority)
00145       ast_set_priority(0);
00146 #if __BYTE_ORDER == __BIG_ENDIAN
00147    for (x = 0; x < length; x += 2) {
00148       c = *(waveform + x + 1);
00149       *(waveform + x + 1) = *(waveform + x);
00150       *(waveform + x) = c;
00151    }
00152 #endif
00153 
00154    if (write(0, waveform, length) < 0) {
00155       /* Cannot log -- all FDs are already closed */
00156    }
00157 
00158    close(fd);
00159    _exit(0);
00160 }

static char* socket_receive_file_to_buff ( int  fd,
int *  size 
) [static]

Definition at line 77 of file app_festival.c.

References ast_free, ast_malloc, ast_realloc, and buff.

Referenced by festival_exec().

00078 {
00079    /* Receive file (probably a waveform file) from socket using
00080     * Festival key stuff technique, but long winded I know, sorry
00081     * but will receive any file without closing the stream or
00082     * using OOB data
00083     */
00084    static char *file_stuff_key = "ft_StUfF_key"; /* must == Festival's key */
00085    char *buff, *tmp;
00086    int bufflen;
00087    int n,k,i;
00088    char c;
00089 
00090    bufflen = 1024;
00091    if (!(buff = ast_malloc(bufflen)))
00092       return NULL;
00093    *size = 0;
00094 
00095    for (k = 0; file_stuff_key[k] != '\0';) {
00096       n = read(fd, &c, 1);
00097       if (n == 0)
00098          break;  /* hit stream eof before end of file */
00099       if ((*size) + k + 1 >= bufflen) {
00100          /* +1 so you can add a terminating NULL if you want */
00101          bufflen += bufflen / 4;
00102          if (!(tmp = ast_realloc(buff, bufflen))) {
00103             ast_free(buff);
00104             return NULL;
00105          }
00106          buff = tmp;
00107       }
00108       if (file_stuff_key[k] == c)
00109          k++;
00110       else if ((c == 'X') && (file_stuff_key[k+1] == '\0')) {
00111          /* It looked like the key but wasn't */
00112          for (i = 0; i < k; i++, (*size)++)
00113             buff[*size] = file_stuff_key[i];
00114          k = 0;
00115          /* omit the stuffed 'X' */
00116       } else {
00117          for (i = 0; i < k; i++, (*size)++)
00118             buff[*size] = file_stuff_key[i];
00119          k = 0;
00120          buff[*size] = c;
00121          (*size)++;
00122       }
00123    }
00124 
00125    return buff;
00126 }

static int unload_module ( void   )  [static]

Definition at line 538 of file app_festival.c.

References ast_unregister_application().

00539 {
00540    return ast_unregister_application(app);
00541 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Simple Festival Interface" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "8586c2a7d357cb591cc3a6607a8f62d1" , .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, } [static]

Definition at line 558 of file app_festival.c.

char* app = "Festival" [static]

Definition at line 75 of file app_festival.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 558 of file app_festival.c.


Generated on Wed Apr 6 11:29:49 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7