Fri Aug 24 02:22:33 2007

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 "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

 AST_MODULE_INFO_STANDARD (ASTERISK_GPL_KEY,"Simple Festival Interface")
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 char * app = "Festival"
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 60 of file app_festival.c.

Referenced by festival_exec(), and load_module().

#define MAXFESTLEN   2048

Definition at line 276 of file app_festival.c.

Referenced by festival_exec().

#define MAXLEN   180

Definition at line 275 of file app_festival.c.


Function Documentation

AST_MODULE_INFO_STANDARD ( ASTERISK_GPL_KEY  ,
"Simple Festival Interface"   
)

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

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

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

static int load_module ( void   )  [static]

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

00543 {
00544    struct ast_config *cfg = ast_config_load(FESTIVAL_CONFIG);
00545    if (!cfg) {
00546       ast_log(LOG_WARNING, "No such configuration file %s\n", FESTIVAL_CONFIG);
00547       return AST_MODULE_LOAD_DECLINE;
00548    }
00549    ast_config_destroy(cfg);
00550    return ast_register_application(app, festival_exec, synopsis, descrip);
00551 }

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

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

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

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

Definition at line 126 of file app_festival.c.

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

Referenced by send_waveform_to_channel().

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

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

Definition at line 72 of file app_festival.c.

References ast_malloc, and ast_realloc.

Referenced by festival_exec().

00073 {
00074     /* Receive file (probably a waveform file) from socket using   */
00075     /* Festival key stuff technique, but long winded I know, sorry */
00076     /* but will receive any file without closing the stream or    */
00077     /* using OOB data                                              */
00078     static char *file_stuff_key = "ft_StUfF_key"; /* must == Festival's key */
00079     char *buff;
00080     int bufflen;
00081     int n,k,i;
00082     char c;
00083 
00084     bufflen = 1024;
00085     if (!(buff = ast_malloc(bufflen)))
00086     {
00087         /* TODO: Handle memory allocation failure */
00088     }
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             if (!(buff = ast_realloc(buff, bufflen)))
00099             {
00100                 /* TODO: Handle memory allocation failure */
00101             }
00102         }
00103         if (file_stuff_key[k] == c)
00104             k++;
00105         else if ((c == 'X') && (file_stuff_key[k+1] == '\0'))
00106         {   /* It looked like the key but wasn't */
00107             for (i=0; i < k; i++,(*size)++)
00108                 buff[*size] = file_stuff_key[i];
00109             k=0;
00110             /* omit the stuffed 'X' */
00111         }
00112         else
00113         {
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 
00123     return buff;
00124 }

static int unload_module ( void   )  [static]

Definition at line 531 of file app_festival.c.

References ast_module_user_hangup_all, and ast_unregister_application().

00532 {
00533    int res;
00534 
00535    res = ast_unregister_application(app);
00536 
00537    ast_module_user_hangup_all();
00538 
00539    return res;
00540 }


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.

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

Definition at line 64 of file app_festival.c.


Generated on Fri Aug 24 02:22:34 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1