#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"
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. | |
int | festival_exec (struct ast_channel *chan, void *vdata) |
char * | key () |
Returns the ASTERISK_GPL_KEY. | |
int | load_module (void) |
Initialize the module. | |
int | send_waveform_to_channel (struct ast_channel *chan, char *waveform, int length, char *intkeys) |
int | send_waveform_to_fd (char *waveform, int length, int fd) |
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 | |
char * | app = "Festival" |
char * | descrip |
LOCAL_USER_DECL | |
STANDARD_LOCAL_USER | |
char * | synopsis = "Say text to the user" |
char * | tdesc = "Simple Festival Interface" |
Definition in file app_festival.c.
|
Definition at line 57 of file app_festival.c. Referenced by festival_exec(). |
|
Definition at line 259 of file app_festival.c. Referenced by festival_exec(). |
|
Definition at line 258 of file app_festival.c. |
|
Provides a description of the module.
Definition at line 517 of file app_festival.c. 00518 {
00519 return tdesc;
00520 }
|
|
Definition at line 264 of file app_festival.c. References ahp, ast_config_destroy(), ast_config_load(), ast_gethostbyname(), ast_log(), ast_strdupa, ast_strlen_zero(), ast_true(), ast_variable_retrieve(), cfg, FESTIVAL_CONFIG, free, LOCAL_USER_ADD, LOCAL_USER_REMOVE, LOG_DEBUG, LOG_ERROR, LOG_WARNING, MAXFESTLEN, MD5Final(), MD5Init(), MD5Update(), send_waveform_to_channel(), and socket_receive_file_to_buff(). Referenced by load_module(). 00265 { 00266 int usecache; 00267 int res=0; 00268 struct localuser *u; 00269 struct sockaddr_in serv_addr; 00270 struct hostent *serverhost; 00271 struct ast_hostent ahp; 00272 int fd; 00273 FILE *fs; 00274 char *host; 00275 char *cachedir; 00276 char *temp; 00277 char *festivalcommand; 00278 int port=1314; 00279 int n; 00280 char ack[4]; 00281 char *waveform; 00282 int filesize; 00283 int wave; 00284 char bigstring[MAXFESTLEN]; 00285 int i; 00286 struct MD5Context md5ctx; 00287 unsigned char MD5Res[16]; 00288 char MD5Hex[33] = ""; 00289 char koko[4] = ""; 00290 char cachefile[MAXFESTLEN]=""; 00291 int readcache=0; 00292 int writecache=0; 00293 int strln; 00294 int fdesc = -1; 00295 char buffer[16384]; 00296 int seekpos = 0; 00297 char *data; 00298 char *intstr; 00299 struct ast_config *cfg; 00300 00301 if (ast_strlen_zero(vdata)) { 00302 ast_log(LOG_WARNING, "festival requires an argument (text)\n"); 00303 return -1; 00304 } 00305 00306 LOCAL_USER_ADD(u); 00307 00308 cfg = ast_config_load(FESTIVAL_CONFIG); 00309 if (!cfg) { 00310 ast_log(LOG_WARNING, "No such configuration file %s\n", FESTIVAL_CONFIG); 00311 LOCAL_USER_REMOVE(u); 00312 return -1; 00313 } 00314 if (!(host = ast_variable_retrieve(cfg, "general", "host"))) { 00315 host = "localhost"; 00316 } 00317 if (!(temp = ast_variable_retrieve(cfg, "general", "port"))) { 00318 port = 1314; 00319 } else { 00320 port = atoi(temp); 00321 } 00322 if (!(temp = ast_variable_retrieve(cfg, "general", "usecache"))) { 00323 usecache=0; 00324 } else { 00325 usecache = ast_true(temp); 00326 } 00327 if (!(cachedir = ast_variable_retrieve(cfg, "general", "cachedir"))) { 00328 cachedir = "/tmp/"; 00329 } 00330 if (!(festivalcommand = ast_variable_retrieve(cfg, "general", "festivalcommand"))) { 00331 festivalcommand = "(tts_textasterisk \"%s\" 'file)(quit)\n"; 00332 } 00333 00334 data = ast_strdupa(vdata); 00335 if (!data) { 00336 ast_log(LOG_ERROR, "Out of memery\n"); 00337 ast_config_destroy(cfg); 00338 LOCAL_USER_REMOVE(u); 00339 return -1; 00340 } 00341 00342 intstr = strchr(data, '|'); 00343 if (intstr) { 00344 *intstr = '\0'; 00345 intstr++; 00346 if (!strcasecmp(intstr, "any")) 00347 intstr = AST_DIGIT_ANY; 00348 } 00349 00350 ast_log(LOG_DEBUG, "Text passed to festival server : %s\n",(char *)data); 00351 /* Connect to local festival server */ 00352 00353 fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 00354 00355 if (fd < 0) { 00356 ast_log(LOG_WARNING,"festival_client: can't get socket\n"); 00357 ast_config_destroy(cfg); 00358 LOCAL_USER_REMOVE(u); 00359 return -1; 00360 } 00361 memset(&serv_addr, 0, sizeof(serv_addr)); 00362 if ((serv_addr.sin_addr.s_addr = inet_addr(host)) == -1) { 00363 /* its a name rather than an ipnum */ 00364 serverhost = ast_gethostbyname(host, &ahp); 00365 if (serverhost == (struct hostent *)0) { 00366 ast_log(LOG_WARNING,"festival_client: gethostbyname failed\n"); 00367 ast_config_destroy(cfg); 00368 LOCAL_USER_REMOVE(u); 00369 return -1; 00370 } 00371 memmove(&serv_addr.sin_addr,serverhost->h_addr, serverhost->h_length); 00372 } 00373 serv_addr.sin_family = AF_INET; 00374 serv_addr.sin_port = htons(port); 00375 00376 if (connect(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) != 0) { 00377 ast_log(LOG_WARNING,"festival_client: connect to server failed\n"); 00378 ast_config_destroy(cfg); 00379 LOCAL_USER_REMOVE(u); 00380 return -1; 00381 } 00382 00383 /* Compute MD5 sum of string */ 00384 MD5Init(&md5ctx); 00385 MD5Update(&md5ctx,(unsigned char const *)data,strlen(data)); 00386 MD5Final(MD5Res,&md5ctx); 00387 MD5Hex[0] = '\0'; 00388 00389 /* Convert to HEX and look if there is any matching file in the cache 00390 directory */ 00391 for (i=0;i<16;i++) { 00392 snprintf(koko, sizeof(koko), "%X",MD5Res[i]); 00393 strncat(MD5Hex, koko, sizeof(MD5Hex) - strlen(MD5Hex) - 1); 00394 } 00395 readcache=0; 00396 writecache=0; 00397 if (strlen(cachedir)+strlen(MD5Hex)+1<=MAXFESTLEN && (usecache==-1)) { 00398 snprintf(cachefile, sizeof(cachefile), "%s/%s", cachedir, MD5Hex); 00399 fdesc=open(cachefile,O_RDWR); 00400 if (fdesc==-1) { 00401 fdesc=open(cachefile,O_CREAT|O_RDWR,0777); 00402 if (fdesc!=-1) { 00403 writecache=1; 00404 strln=strlen((char *)data); 00405 ast_log(LOG_DEBUG,"line length : %d\n",strln); 00406 write(fdesc,&strln,sizeof(int)); 00407 write(fdesc,data,strln); 00408 seekpos=lseek(fdesc,0,SEEK_CUR); 00409 ast_log(LOG_DEBUG,"Seek position : %d\n",seekpos); 00410 } 00411 } else { 00412 read(fdesc,&strln,sizeof(int)); 00413 ast_log(LOG_DEBUG,"Cache file exists, strln=%d, strlen=%d\n",strln,(int)strlen((char *)data)); 00414 if (strlen((char *)data)==strln) { 00415 ast_log(LOG_DEBUG,"Size OK\n"); 00416 read(fdesc,&bigstring,strln); 00417 bigstring[strln] = 0; 00418 if (strcmp(bigstring,data)==0) { 00419 readcache=1; 00420 } else { 00421 ast_log(LOG_WARNING,"Strings do not match\n"); 00422 } 00423 } else { 00424 ast_log(LOG_WARNING,"Size mismatch\n"); 00425 } 00426 } 00427 } 00428 00429 if (readcache==1) { 00430 close(fd); 00431 fd=fdesc; 00432 ast_log(LOG_DEBUG,"Reading from cache...\n"); 00433 } else { 00434 ast_log(LOG_DEBUG,"Passing text to festival...\n"); 00435 fs=fdopen(dup(fd),"wb"); 00436 fprintf(fs,festivalcommand,(char *)data); 00437 fflush(fs); 00438 fclose(fs); 00439 } 00440 00441 /* Write to cache and then pass it down */ 00442 if (writecache==1) { 00443 ast_log(LOG_DEBUG,"Writing result to cache...\n"); 00444 while ((strln=read(fd,buffer,16384))!=0) { 00445 write(fdesc,buffer,strln); 00446 } 00447 close(fd); 00448 close(fdesc); 00449 fd=open(cachefile,O_RDWR); 00450 lseek(fd,seekpos,SEEK_SET); 00451 } 00452 00453 ast_log(LOG_DEBUG,"Passing data to channel...\n"); 00454 00455 /* Read back info from server */ 00456 /* This assumes only one waveform will come back, also LP is unlikely */ 00457 wave = 0; 00458 do { 00459 int read_data; 00460 for (n=0; n < 3; ) 00461 { 00462 read_data = read(fd,ack+n,3-n); 00463 /* this avoids falling in infinite loop 00464 * in case that festival server goes down 00465 * */ 00466 if ( read_data == -1 ) 00467 { 00468 ast_log(LOG_WARNING,"Unable to read from cache/festival fd"); 00469 return -1; 00470 } 00471 n += read_data; 00472 } 00473 ack[3] = '\0'; 00474 if (strcmp(ack,"WV\n") == 0) { /* receive a waveform */ 00475 ast_log(LOG_DEBUG,"Festival WV command\n"); 00476 waveform = socket_receive_file_to_buff(fd,&filesize); 00477 res = send_waveform_to_channel(chan,waveform,filesize, intstr); 00478 free(waveform); 00479 break; 00480 } 00481 else if (strcmp(ack,"LP\n") == 0) { /* receive an s-expr */ 00482 ast_log(LOG_DEBUG,"Festival LP command\n"); 00483 waveform = socket_receive_file_to_buff(fd,&filesize); 00484 waveform[filesize]='\0'; 00485 ast_log(LOG_WARNING,"Festival returned LP : %s\n",waveform); 00486 free(waveform); 00487 } else if (strcmp(ack,"ER\n") == 0) { /* server got an error */ 00488 ast_log(LOG_WARNING,"Festival returned ER\n"); 00489 res=-1; 00490 break; 00491 } 00492 } while (strcmp(ack,"OK\n") != 0); 00493 close(fd); 00494 ast_config_destroy(cfg); 00495 LOCAL_USER_REMOVE(u); 00496 return res; 00497 00498 }
|
|
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 529 of file app_festival.c. 00530 {
00531 return ASTERISK_GPL_KEY;
00532 }
|
|
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.
Definition at line 511 of file app_festival.c. References app, ast_register_application(), descrip, festival_exec(), and synopsis. 00512 { 00513 00514 return ast_register_application(app, festival_exec, synopsis, descrip); 00515 }
|
|
Definition at line 155 of file app_festival.c. References ast_channel::_state, ast_answer(), AST_FORMAT_SLINEAR, ast_frfree(), ast_indicate(), ast_log(), ast_read(), ast_set_write_format(), ast_stopstream(), ast_waitfor(), ast_write(), ast_frame::data, ast_frame::datalen, ast_frame::frametype, LOG_DEBUG, LOG_WARNING, ast_frame::mallocd, ast_frame::offset, ast_frame::samples, send_waveform_to_fd(), ast_frame::src, ast_frame::subclass, and ast_channel::writeformat. Referenced by festival_exec(). 00155 { 00156 int res=0; 00157 int fds[2]; 00158 int ms = -1; 00159 int pid = -1; 00160 int needed = 0; 00161 int owriteformat; 00162 struct ast_frame *f; 00163 struct myframe { 00164 struct ast_frame f; 00165 char offset[AST_FRIENDLY_OFFSET]; 00166 char frdata[2048]; 00167 } myf; 00168 00169 if (pipe(fds)) { 00170 ast_log(LOG_WARNING, "Unable to create pipe\n"); 00171 return -1; 00172 } 00173 00174 /* Answer if it's not already going */ 00175 if (chan->_state != AST_STATE_UP) 00176 ast_answer(chan); 00177 ast_stopstream(chan); 00178 ast_indicate(chan, -1); 00179 00180 owriteformat = chan->writeformat; 00181 res = ast_set_write_format(chan, AST_FORMAT_SLINEAR); 00182 if (res < 0) { 00183 ast_log(LOG_WARNING, "Unable to set write format to signed linear\n"); 00184 return -1; 00185 } 00186 00187 res=send_waveform_to_fd(waveform,length,fds[1]); 00188 if (res >= 0) { 00189 pid = res; 00190 /* Order is important -- there's almost always going to be mp3... we want to prioritize the 00191 user */ 00192 for (;;) { 00193 ms = 1000; 00194 res = ast_waitfor(chan, ms); 00195 if (res < 1) { 00196 res = -1; 00197 break; 00198 } 00199 f = ast_read(chan); 00200 if (!f) { 00201 ast_log(LOG_WARNING, "Null frame == hangup() detected\n"); 00202 res = -1; 00203 break; 00204 } 00205 if (f->frametype == AST_FRAME_DTMF) { 00206 ast_log(LOG_DEBUG, "User pressed a key\n"); 00207 if (intkeys && strchr(intkeys, f->subclass)) { 00208 res = f->subclass; 00209 ast_frfree(f); 00210 break; 00211 } 00212 } 00213 if (f->frametype == AST_FRAME_VOICE) { 00214 /* Treat as a generator */ 00215 needed = f->samples * 2; 00216 if (needed > sizeof(myf.frdata)) { 00217 ast_log(LOG_WARNING, "Only able to deliver %d of %d requested samples\n", 00218 (int)sizeof(myf.frdata) / 2, needed/2); 00219 needed = sizeof(myf.frdata); 00220 } 00221 res = read(fds[0], myf.frdata, needed); 00222 if (res > 0) { 00223 myf.f.frametype = AST_FRAME_VOICE; 00224 myf.f.subclass = AST_FORMAT_SLINEAR; 00225 myf.f.datalen = res; 00226 myf.f.samples = res / 2; 00227 myf.f.mallocd = 0; 00228 myf.f.offset = AST_FRIENDLY_OFFSET; 00229 myf.f.src = __PRETTY_FUNCTION__; 00230 myf.f.data = myf.frdata; 00231 if (ast_write(chan, &myf.f) < 0) { 00232 res = -1; 00233 break; 00234 } 00235 if (res < needed) { /* last frame */ 00236 ast_log(LOG_DEBUG, "Last frame\n"); 00237 res=0; 00238 break; 00239 } 00240 } else { 00241 ast_log(LOG_DEBUG, "No more waveform\n"); 00242 res = 0; 00243 } 00244 } 00245 ast_frfree(f); 00246 } 00247 } 00248 close(fds[0]); 00249 close(fds[1]); 00250 00251 /* if (pid > -1) */ 00252 /* kill(pid, SIGKILL); */ 00253 if (!res && owriteformat) 00254 ast_set_write_format(chan, owriteformat); 00255 return res; 00256 }
|
|
Definition at line 122 of file app_festival.c. References ast_log(), and LOG_WARNING. Referenced by send_waveform_to_channel(). 00122 { 00123 00124 int res; 00125 int x; 00126 #ifdef __PPC__ 00127 char c; 00128 #endif 00129 00130 res = fork(); 00131 if (res < 0) 00132 ast_log(LOG_WARNING, "Fork failed\n"); 00133 if (res) 00134 return res; 00135 for (x=0;x<256;x++) { 00136 if (x != fd) 00137 close(x); 00138 } 00139 /*IAS */ 00140 #ifdef __PPC__ 00141 for( x=0; x<length; x+=2) 00142 { 00143 c = *(waveform+x+1); 00144 *(waveform+x+1)=*(waveform+x); 00145 *(waveform+x)=c; 00146 } 00147 #endif 00148 00149 write(fd,waveform,length); 00150 close(fd); 00151 exit(0); 00152 }
|
|
Definition at line 74 of file app_festival.c. References malloc, and realloc. 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 closeing the stream or */ 00079 /* using OOB data */ 00080 static char *file_stuff_key = "ft_StUfF_key"; /* must == Festival's key */ 00081 char *buff; 00082 int bufflen; 00083 int n,k,i; 00084 char c; 00085 00086 bufflen = 1024; 00087 buff = (char *)malloc(bufflen); 00088 *size=0; 00089 00090 for (k=0; file_stuff_key[k] != '\0';) 00091 { 00092 n = read(fd,&c,1); 00093 if (n==0) break; /* hit stream eof before end of file */ 00094 if ((*size)+k+1 >= bufflen) 00095 { /* +1 so you can add a NULL if you want */ 00096 bufflen += bufflen/4; 00097 buff = (char *)realloc(buff,bufflen); 00098 } 00099 if (file_stuff_key[k] == c) 00100 k++; 00101 else if ((c == 'X') && (file_stuff_key[k+1] == '\0')) 00102 { /* It looked like the key but wasn't */ 00103 for (i=0; i < k; i++,(*size)++) 00104 buff[*size] = file_stuff_key[i]; 00105 k=0; 00106 /* omit the stuffed 'X' */ 00107 } 00108 else 00109 { 00110 for (i=0; i < k; i++,(*size)++) 00111 buff[*size] = file_stuff_key[i]; 00112 k=0; 00113 buff[*size] = c; 00114 (*size)++; 00115 } 00116 00117 } 00118 00119 return buff; 00120 }
|
|
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 500 of file app_festival.c. References app, and ast_unregister_application(). 00501 { 00502 int res; 00503 00504 res = ast_unregister_application(app); 00505 00506 STANDARD_HANGUP_LOCALUSERS; 00507 00508 return res; 00509 }
|
|
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 522 of file app_festival.c. References STANDARD_USECOUNT. 00523 { 00524 int res; 00525 STANDARD_USECOUNT(res); 00526 return res; 00527 }
|
|
Definition at line 61 of file app_festival.c. Referenced by load_module(), and unload_module(). |
|
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 65 of file app_festival.c. Referenced by load_module(). |
|
Definition at line 72 of file app_festival.c. |
|
Definition at line 70 of file app_festival.c. |
|
Definition at line 63 of file app_festival.c. Referenced by load_module(). |
|
Definition at line 59 of file app_festival.c. |