#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" |
Definition in file app_festival.c.
| #define FESTIVAL_CONFIG "festival.conf" |
| #define MAXFESTLEN 2048 |
| #define MAXLEN 180 |
Definition at line 271 of file app_festival.c.
| char* description | ( | void | ) |
Provides a description of the 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; }
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.
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).
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.
Definition at line 538 of file app_festival.c.
References STANDARD_USECOUNT.
00539 { 00540 int res; 00541 STANDARD_USECOUNT(res); 00542 return res; 00543 }
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.
Definition at line 64 of file app_festival.c.
char* tdesc = "Simple Festival Interface" [static] |
Definition at line 60 of file app_festival.c.
1.5.6