Wed Oct 28 15:48:04 2009

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"

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

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 272 of file app_festival.c.

Referenced by festival_exec().

#define MAXLEN   180

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

00534 {
00535    return tdesc;
00536 }

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

Definition at line 277 of file app_festival.c.

References 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().

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

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 545 of file app_festival.c.

References ASTERISK_GPL_KEY.

00546 {
00547    return ASTERISK_GPL_KEY;
00548 }

int load_module ( void   ) 

Initialize the module.

This function is called at module load time. Put all code in here that needs to set up your module's hardware, software, registrations, etc.

Returns:
This function should return 0 on success and non-zero on failure. If the module is not loaded successfully, Asterisk will call its unload_module() function.
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.
TE STUFF END

Definition at line 527 of file app_festival.c.

References ast_register_application(), and festival_exec().

00528 {
00529    
00530    return ast_register_application(app, festival_exec, synopsis, descrip);
00531 }

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_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(), ast_frame::subclass, and ast_channel::writeformat.

Referenced by festival_exec().

00166                                                                                                          {
00167    int res=0;
00168    int fds[2];
00169    int ms = -1;
00170    int pid = -1;
00171    int needed = 0;
00172    int owriteformat;
00173    struct ast_frame *f;
00174    struct myframe {
00175       struct ast_frame f;
00176       char offset[AST_FRIENDLY_OFFSET];
00177       char frdata[2048];
00178    } myf;
00179    
00180         if (pipe(fds)) {
00181                  ast_log(LOG_WARNING, "Unable to create pipe\n");
00182          return -1;
00183         }
00184                                                    
00185    /* Answer if it's not already going */
00186    if (chan->_state != AST_STATE_UP)
00187       ast_answer(chan);
00188    ast_stopstream(chan);
00189    ast_indicate(chan, -1);
00190    
00191    owriteformat = chan->writeformat;
00192    res = ast_set_write_format(chan, AST_FORMAT_SLINEAR);
00193    if (res < 0) {
00194       ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
00195       return -1;
00196    }
00197    
00198    res=send_waveform_to_fd(waveform,length,fds[1]);
00199    if (res >= 0) {
00200       pid = res;
00201       /* Order is important -- there's almost always going to be mp3...  we want to prioritize the
00202          user */
00203       for (;;) {
00204          ms = 1000;
00205          res = ast_waitfor(chan, ms);
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_log(LOG_DEBUG, "User pressed a key\n");
00218             if (intkeys && strchr(intkeys, f->subclass)) {
00219                res = f->subclass;
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                myf.f.subclass = AST_FORMAT_SLINEAR;
00236                myf.f.datalen = res;
00237                myf.f.samples = res / 2;
00238                myf.f.mallocd = 0;
00239                myf.f.offset = AST_FRIENDLY_OFFSET;
00240                myf.f.src = __PRETTY_FUNCTION__;
00241                myf.f.data = myf.frdata;
00242                if (ast_write(chan, &myf.f) < 0) {
00243                   res = -1;
00244                   ast_frfree(f);
00245                   break;
00246                }
00247                if (res < needed) { /* last frame */
00248                   ast_log(LOG_DEBUG, "Last frame\n");
00249                   res=0;
00250                   ast_frfree(f);
00251                   break;
00252                }
00253             } else {
00254                ast_log(LOG_DEBUG, "No more waveform\n");
00255                res = 0;
00256             }
00257          }
00258          ast_frfree(f);
00259       }
00260    }
00261    close(fds[0]);
00262    close(fds[1]);
00263 
00264 /* if (pid > -1) */
00265 /*    kill(pid, SIGKILL); */
00266    if (!res && owriteformat)
00267       ast_set_write_format(chan, owriteformat);
00268    return res;
00269 }

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    sigset_t fullset, oldset;
00131 
00132    sigfillset(&fullset);
00133    pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
00134 
00135         res = fork();
00136         if (res < 0)
00137                 ast_log(LOG_WARNING, "Fork failed\n");
00138         if (res) {
00139       pthread_sigmask(SIG_SETMASK, &oldset, NULL);
00140                 return res;
00141    }
00142         for (x=0;x<256;x++) {
00143                 if (x != fd)
00144                         close(x);
00145         }
00146    if (option_highpriority)
00147       ast_set_priority(0);
00148    signal(SIGPIPE, SIG_DFL);
00149    pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
00150 /*IAS */
00151 #ifdef __PPC__  
00152    for( x=0; x<length; x+=2)
00153    {
00154       c = *(waveform+x+1);
00155       *(waveform+x+1)=*(waveform+x);
00156       *(waveform+x)=c;
00157    }
00158 #endif
00159    
00160    write(fd,waveform,length);
00161    close(fd);
00162    exit(0);
00163 }

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 516 of file app_festival.c.

References ast_unregister_application(), and STANDARD_HANGUP_LOCALUSERS.

00517 {
00518    int res;
00519 
00520    res = ast_unregister_application(app);
00521 
00522    STANDARD_HANGUP_LOCALUSERS;
00523 
00524    return res;
00525 }

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 538 of file app_festival.c.

References STANDARD_USECOUNT.

00539 {
00540    int res;
00541    STANDARD_USECOUNT(res);
00542    return res;
00543 }


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.

Definition at line 73 of file app_festival.c.

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 Wed Oct 28 15:48:04 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6