Thu Feb 9 06:34:18 2012

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"

Include dependency graph for app_festival.c:

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 = AST_BUILDOPT_SUM, .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 58 of file app_festival.c.

Referenced by festival_exec(), and load_module().

#define MAXFESTLEN   2048

Definition at line 60 of file app_festival.c.

Referenced by festival_exec().

#define MAXLEN   180

Definition at line 59 of file app_festival.c.


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 555 of file app_festival.c.

static void __unreg_module ( void   )  [static]

Definition at line 555 of file app_festival.c.

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

Definition at line 268 of file app_festival.c.

References 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_STATUS_FILEINVALID, errno, FESTIVAL_CONFIG, LOG_ERROR, LOG_WARNING, MAXFESTLEN, MD5Final(), MD5Init(), MD5Update(), send_waveform_to_channel(), socket_receive_file_to_buff(), and text.

Referenced by load_module().

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

static int load_module ( void   )  [static]

Definition at line 540 of file app_festival.c.

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

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

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

Definition at line 166 of file app_festival.c.

References ast_channel::_state, ast_answer(), ast_debug, ast_format_clear(), ast_format_copy(), ast_format_set(), 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_set_write_format_by_id(), AST_STATE_UP, ast_stopstream(), ast_waitfor(), ast_write(), f, ast_frame::frametype, ast_format::id, ast_frame_subclass::integer, LOG_WARNING, ast_frame::offset, ast_frame::samples, send_waveform_to_fd(), ast_frame::subclass, and ast_channel::writeformat.

Referenced by festival_exec().

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

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

Definition at line 132 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().

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

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

Definition at line 81 of file app_festival.c.

References ast_free, ast_malloc, ast_realloc, and buff.

Referenced by festival_exec().

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

static int unload_module ( void   )  [static]

Definition at line 535 of file app_festival.c.

References ast_unregister_application().

00536 {
00537    return ast_unregister_application(app);
00538 }


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 = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, } [static]

Definition at line 555 of file app_festival.c.

char* app = "Festival" [static]

Definition at line 79 of file app_festival.c.

Definition at line 555 of file app_festival.c.


Generated on Thu Feb 9 06:34:19 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6