Sat Aug 6 00:39:34 2011

Asterisk developer's documentation


app_festival.c File Reference

Connect to festival. More...

#include "asterisk.h"
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <errno.h>
#include <sys/capability.h>
#include "asterisk/file.h"
#include "asterisk/logger.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/options.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, void *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_DEFAULT | AST_MODFLAG_BUILDSUM, .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 = "361d7bb937402d51e4658efb5b4d76e4" , .load = load_module, .unload = unload_module, }
static char * app = "Festival"
static const struct ast_module_infoast_module_info = &__mod_info
static char * descrip
static char * synopsis = "Say text to the user"


Detailed Description

Connect to festival.

Author:
Christos Ricudis <ricudis@itc.auth.gr>

Definition in file app_festival.c.


Define Documentation

#define FESTIVAL_CONFIG   "festival.conf"

Definition at line 68 of file app_festival.c.

Referenced by festival_exec(), and load_module().

#define MAXFESTLEN   2048

Definition at line 298 of file app_festival.c.

Referenced by festival_exec().

#define MAXLEN   180

Definition at line 297 of file app_festival.c.


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 585 of file app_festival.c.

static void __unreg_module ( void   )  [static]

Definition at line 585 of file app_festival.c.

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

Definition at line 303 of file app_festival.c.

References ahp, ast_config_destroy(), ast_config_load(), AST_DIGIT_ANY, ast_gethostbyname(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_strdupa, ast_strlen_zero(), ast_true(), ast_variable_retrieve(), errno, FESTIVAL_CONFIG, free, LOG_DEBUG, LOG_WARNING, MAXFESTLEN, MD5Final(), MD5Init(), MD5Update(), send_waveform_to_channel(), socket_receive_file_to_buff(), and wave.

Referenced by load_module().

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

static int load_module ( void   )  [static]

Definition at line 574 of file app_festival.c.

References ast_config_destroy(), ast_config_load(), ast_log(), AST_MODULE_LOAD_DECLINE, ast_register_application(), FESTIVAL_CONFIG, festival_exec(), and LOG_WARNING.

00575 {
00576    struct ast_config *cfg = ast_config_load(FESTIVAL_CONFIG);
00577    if (!cfg) {
00578       ast_log(LOG_WARNING, "No such configuration file %s\n", FESTIVAL_CONFIG);
00579       return AST_MODULE_LOAD_DECLINE;
00580    }
00581    ast_config_destroy(cfg);
00582    return ast_register_application(app, festival_exec, synopsis, descrip);
00583 }

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

Definition at line 191 of file app_festival.c.

References ast_channel::_state, ast_answer(), 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, ast_channel::fds, LOG_DEBUG, LOG_WARNING, offset, and send_waveform_to_fd().

Referenced by festival_exec().

00191                                                                                                          {
00192    int res=0;
00193    int fds[2];
00194    int ms = -1;
00195    int pid = -1;
00196    int needed = 0;
00197    int owriteformat;
00198    struct ast_frame *f;
00199    struct myframe {
00200       struct ast_frame f;
00201       char offset[AST_FRIENDLY_OFFSET];
00202       char frdata[2048];
00203    } myf = {
00204       .f = { 0, },
00205    };
00206    
00207         if (pipe(fds)) {
00208                  ast_log(LOG_WARNING, "Unable to create pipe\n");
00209          return -1;
00210         }
00211                                                    
00212    /* Answer if it's not already going */
00213    if (chan->_state != AST_STATE_UP)
00214       ast_answer(chan);
00215    ast_stopstream(chan);
00216    ast_indicate(chan, -1);
00217    
00218    owriteformat = chan->writeformat;
00219    res = ast_set_write_format(chan, AST_FORMAT_SLINEAR);
00220    if (res < 0) {
00221       ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
00222       return -1;
00223    }
00224    
00225    res=send_waveform_to_fd(waveform,length,fds[1]);
00226    if (res >= 0) {
00227       pid = res;
00228       /* Order is important -- there's almost always going to be mp3...  we want to prioritize the
00229          user */
00230       for (;;) {
00231          ms = 1000;
00232          res = ast_waitfor(chan, ms);
00233          if (res < 1) {
00234             res = -1;
00235             break;
00236          }
00237          f = ast_read(chan);
00238          if (!f) {
00239             ast_log(LOG_WARNING, "Null frame == hangup() detected\n");
00240             res = -1;
00241             break;
00242          }
00243          if (f->frametype == AST_FRAME_DTMF) {
00244             ast_log(LOG_DEBUG, "User pressed a key\n");
00245             if (intkeys && strchr(intkeys, f->subclass)) {
00246                res = f->subclass;
00247                ast_frfree(f);
00248                break;
00249             }
00250          }
00251          if (f->frametype == AST_FRAME_VOICE) {
00252             /* Treat as a generator */
00253             needed = f->samples * 2;
00254             if (needed > sizeof(myf.frdata)) {
00255                ast_log(LOG_WARNING, "Only able to deliver %d of %d requested samples\n",
00256                   (int)sizeof(myf.frdata) / 2, needed/2);
00257                needed = sizeof(myf.frdata);
00258             }
00259             res = read(fds[0], myf.frdata, needed);
00260             if (res > 0) {
00261                myf.f.frametype = AST_FRAME_VOICE;
00262                myf.f.subclass = AST_FORMAT_SLINEAR;
00263                myf.f.datalen = res;
00264                myf.f.samples = res / 2;
00265                myf.f.offset = AST_FRIENDLY_OFFSET;
00266                myf.f.src = __PRETTY_FUNCTION__;
00267                myf.f.data = myf.frdata;
00268                if (ast_write(chan, &myf.f) < 0) {
00269                   res = -1;
00270                   ast_frfree(f);
00271                   break;
00272                }
00273                if (res < needed) { /* last frame */
00274                   ast_log(LOG_DEBUG, "Last frame\n");
00275                   res=0;
00276                   ast_frfree(f);
00277                   break;
00278                }
00279             } else {
00280                ast_log(LOG_DEBUG, "No more waveform\n");
00281                res = 0;
00282             }
00283          }
00284          ast_frfree(f);
00285       }
00286    }
00287    close(fds[0]);
00288    close(fds[1]);
00289 
00290 /* if (pid > -1) */
00291 /*    kill(pid, SIGKILL); */
00292    if (!res && owriteformat)
00293       ast_set_write_format(chan, owriteformat);
00294    return res;
00295 }

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

Definition at line 134 of file app_festival.c.

References ast_log(), ast_opt_high_priority, ast_set_priority(), errno, and LOG_WARNING.

Referenced by send_waveform_to_channel().

00134                                                                    {
00135 
00136         int res;
00137         int x;
00138 #ifdef __PPC__ 
00139    char c;
00140 #endif
00141    sigset_t fullset, oldset;
00142 #ifdef HAVE_CAP
00143    cap_t cap;
00144 #endif
00145 
00146    sigfillset(&fullset);
00147    pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
00148 
00149    res = fork();
00150    if (res < 0)
00151       ast_log(LOG_WARNING, "Fork failed\n");
00152    if (res) {
00153       pthread_sigmask(SIG_SETMASK, &oldset, NULL);
00154       return res;
00155    }
00156 #ifdef HAVE_CAP
00157    cap = cap_from_text("cap_net_admin-eip");
00158 
00159    if (cap_set_proc(cap)) {
00160       /* Careful with order! Logging cannot happen after we close FDs */
00161       ast_log(LOG_WARNING, "Unable to remove capabilities.\n");
00162    }
00163    cap_free(cap);
00164 #endif
00165    for (x=0;x<256;x++) {
00166       if (x != fd)
00167          close(x);
00168    }
00169    if (ast_opt_high_priority)
00170       ast_set_priority(0);
00171    signal(SIGPIPE, SIG_DFL);
00172    pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
00173 /*IAS */
00174 #ifdef __PPC__  
00175    for( x=0; x<length; x+=2)
00176    {
00177       c = *(waveform+x+1);
00178       *(waveform+x+1)=*(waveform+x);
00179       *(waveform+x)=c;
00180    }
00181 #endif
00182    
00183    if (write(fd,waveform,length) < 0) {
00184       ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
00185    }
00186    close(fd);
00187    exit(0);
00188 }

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

Definition at line 80 of file app_festival.c.

References ast_malloc, and ast_realloc.

Referenced by festival_exec().

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

static int unload_module ( void   )  [static]

Definition at line 563 of file app_festival.c.

References ast_module_user_hangup_all, and ast_unregister_application().

00564 {
00565    int res;
00566 
00567    res = ast_unregister_application(app);
00568 
00569    ast_module_user_hangup_all();
00570 
00571    return res;
00572 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_BUILDSUM, .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 = "361d7bb937402d51e4658efb5b4d76e4" , .load = load_module, .unload = unload_module, } [static]

Definition at line 585 of file app_festival.c.

char* app = "Festival" [static]

Definition at line 70 of file app_festival.c.

const struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 585 of file app_festival.c.

char* descrip [static]

Initial value:

 
"  Festival(text[|intkeys]):  Connect to Festival, send the argument, get back the waveform,"
"play it to the user, allowing any given interrupt keys to immediately terminate and return\n"
"the value, or 'any' to allow any number back (useful in dialplan)\n"

Definition at line 74 of file app_festival.c.

char* synopsis = "Say text to the user" [static]

Definition at line 72 of file app_festival.c.


Generated on Sat Aug 6 00:39:34 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7