Thu Jul 9 13:40:44 2009

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 <sys/capability.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"

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 , .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 = "068e67f60f50dd9ee86464c05884a49d" , .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>
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 60 of file app_festival.c.

Referenced by festival_exec(), and load_module().

#define MAXFESTLEN   2048

Definition at line 62 of file app_festival.c.

Referenced by festival_exec().

#define MAXLEN   180

Definition at line 61 of file app_festival.c.


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 569 of file app_festival.c.

static void __unreg_module ( void   )  [static]

Definition at line 569 of file app_festival.c.

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

Definition at line 287 of file app_festival.c.

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

Referenced by load_module().

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

static int load_module ( void   )  [static]

Definition at line 557 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.

00558 {
00559    struct ast_flags config_flags = { 0 };
00560    struct ast_config *cfg = ast_config_load(FESTIVAL_CONFIG, config_flags);
00561    if (!cfg) {
00562       ast_log(LOG_WARNING, "No such configuration file %s\n", FESTIVAL_CONFIG);
00563       return AST_MODULE_LOAD_DECLINE;
00564    }
00565    ast_config_destroy(cfg);
00566    return ast_register_application(app, festival_exec, synopsis, descrip);
00567 }

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

Definition at line 180 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(), chan, f, LOG_WARNING, ast_frame::offset, and send_waveform_to_fd().

Referenced by festival_exec().

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

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

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

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

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

Definition at line 74 of file app_festival.c.

References ast_free, ast_malloc, ast_realloc, and buff.

Referenced by festival_exec().

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

static int unload_module ( void   )  [static]

Definition at line 552 of file app_festival.c.

References ast_unregister_application().

00553 {
00554    return ast_unregister_application(app);
00555 }


Variable Documentation

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

Definition at line 569 of file app_festival.c.

char* app = "Festival" [static]

Definition at line 64 of file app_festival.c.

const struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 569 of file app_festival.c.

char* descrip [static]

Initial value:

 
"  Festival(text[,intkeys]):  Connect to Festival, send the argument, get back the waveform,\n"
"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 68 of file app_festival.c.

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

Definition at line 66 of file app_festival.c.


Generated on Thu Jul 9 13:40:44 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7