Mon Sep 18 09:13:39 2006

Asterisk developer's documentation


app_festival.c File Reference

Connect to festival. More...

#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 "asterisk.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

char * description (void)
 Provides a description of the module.
static int festival_exec (struct ast_channel *chan, void *vdata)
char * key ()
 Returns the ASTERISK_GPL_KEY.
int load_module (void)
 Initialize the module.
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)
int unload_module (void)
 Cleanup all module structures, sockets, etc.
int usecount (void)
 Provides a usecount.

Variables

static char * app = "Festival"
static char * descrip
 LOCAL_USER_DECL
 STANDARD_LOCAL_USER
static char * synopsis = "Say text to the user"
static char * tdesc = "Simple Festival Interface"


Detailed Description

Connect to 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().

#define MAXFESTLEN   2048

Definition at line 265 of file app_festival.c.

Referenced by festival_exec().

#define MAXLEN   180

Definition at line 264 of file app_festival.c.


Function Documentation

char* description ( void   ) 

Provides a description of the module.

Returns:
a short description of your module

Definition at line 526 of file app_festival.c.

00527 {
00528    return tdesc;
00529 }

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

Definition at line 270 of file app_festival.c.

References ahp, ast_config_destroy(), ast_config_load(), AST_DIGIT_ANY, ast_gethostbyname(), ast_log(), ast_strdupa, ast_strlen_zero(), ast_true(), ast_variable_retrieve(), cfg, FESTIVAL_CONFIG, free, host, LOCAL_USER_ADD, LOCAL_USER_REMOVE, LOG_DEBUG, LOG_ERROR, LOG_WARNING, MAXFESTLEN, MD5Final(), MD5Init(), MD5Update(), n, send_waveform_to_channel(), socket_receive_file_to_buff(), and wave.

Referenced by load_module().

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

char* key ( void   ) 

Returns the ASTERISK_GPL_KEY.

This returns the ASTERISK_GPL_KEY, signifiying that you agree to the terms of the GPL stated in the ASTERISK_GPL_KEY. Your module will not load if it does not return the EXACT message:

 char *key(void) {
         return ASTERISK_GPL_KEY;
 }

Returns:
ASTERISK_GPL_KEY

Definition at line 538 of file app_festival.c.

References ASTERISK_GPL_KEY.

00539 {
00540    return ASTERISK_GPL_KEY;
00541 }

int load_module ( void   ) 

Initialize the module.

Initialize the Agents module. This function is being called by Asterisk when loading the module. Among other thing it registers applications, cli commands and reads the cofiguration file.

Returns:
int Always 0.

Definition at line 520 of file app_festival.c.

References ast_register_application(), and festival_exec().

00521 {
00522    
00523    return ast_register_application(app, festival_exec, synopsis, descrip);
00524 }

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

Definition at line 159 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(), ast_channel::fds, ast_frame::frametype, LOG_DEBUG, LOG_WARNING, offset, ast_frame::samples, send_waveform_to_fd(), and ast_frame::subclass.

Referenced by festival_exec().

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

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

Definition at line 123 of file app_festival.c.

References ast_log(), ast_set_priority(), LOG_WARNING, and option_highpriority.

Referenced by send_waveform_to_channel().

00123                                                                    {
00124 
00125         int res;
00126         int x;
00127 #ifdef __PPC__ 
00128    char c;
00129 #endif
00130 
00131         res = fork();
00132         if (res < 0)
00133                 ast_log(LOG_WARNING, "Fork failed\n");
00134         if (res)
00135                 return res;
00136         for (x=0;x<256;x++) {
00137                 if (x != fd)
00138                         close(x);
00139         }
00140    if (option_highpriority)
00141       ast_set_priority(0);
00142 
00143 /*IAS */
00144 #ifdef __PPC__  
00145    for( x=0; x<length; x+=2)
00146    {
00147       c = *(waveform+x+1);
00148       *(waveform+x+1)=*(waveform+x);
00149       *(waveform+x)=c;
00150    }
00151 #endif
00152    
00153    write(fd,waveform,length);
00154    close(fd);
00155    exit(0);
00156 }

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

Definition at line 75 of file app_festival.c.

References malloc, n, and realloc.

Referenced by festival_exec().

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

int unload_module ( void   ) 

Cleanup all module structures, sockets, etc.

This is called at exit. Any registrations and memory allocations need to be unregistered and free'd here. Nothing else will do these for you (until exit).

Returns:
Zero on success, or non-zero on error.

Definition at line 509 of file app_festival.c.

References ast_unregister_application(), and STANDARD_HANGUP_LOCALUSERS.

00510 {
00511    int res;
00512 
00513    res = ast_unregister_application(app);
00514 
00515    STANDARD_HANGUP_LOCALUSERS;
00516 
00517    return res;
00518 }

int usecount ( void   ) 

Provides a usecount.

This function will be called by various parts of asterisk. Basically, all it has to do is to return a usecount when called. You will need to maintain your usecount within the module somewhere. The usecount should be how many channels provided by this module are in use.

Returns:
The module's usecount.

Definition at line 531 of file app_festival.c.

References STANDARD_USECOUNT.

00532 {
00533    int res;
00534    STANDARD_USECOUNT(res);
00535    return res;
00536 }


Variable Documentation

char* app = "Festival" [static]

Definition at line 62 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 66 of file app_festival.c.

LOCAL_USER_DECL

Definition at line 73 of file app_festival.c.

STANDARD_LOCAL_USER

Definition at line 71 of file app_festival.c.

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

Definition at line 64 of file app_festival.c.

char* tdesc = "Simple Festival Interface" [static]

Definition at line 60 of file app_festival.c.


Generated on Mon Sep 18 09:13:39 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.7