#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" |
Definition in file app_festival.c.
#define FESTIVAL_CONFIG "festival.conf" |
#define MAXFESTLEN 2048 |
#define MAXLEN 180 |
Definition at line 274 of file app_festival.c.
AST_MODULE_INFO_STANDARD | ( | ASTERISK_GPL_KEY | , | |
"Simple Festival Interface" | ||||
) |
static int festival_exec | ( | struct ast_channel * | chan, | |
void * | vdata | |||
) | [static] |
Definition at line 280 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().
00281 { 00282 int usecache; 00283 int res=0; 00284 struct ast_module_user *u; 00285 struct sockaddr_in serv_addr; 00286 struct hostent *serverhost; 00287 struct ast_hostent ahp; 00288 int fd; 00289 FILE *fs; 00290 const char *host; 00291 const char *cachedir; 00292 const char *temp; 00293 const char *festivalcommand; 00294 int port=1314; 00295 int n; 00296 char ack[4]; 00297 char *waveform; 00298 int filesize; 00299 int wave; 00300 char bigstring[MAXFESTLEN]; 00301 int i; 00302 struct MD5Context md5ctx; 00303 unsigned char MD5Res[16]; 00304 char MD5Hex[33] = ""; 00305 char koko[4] = ""; 00306 char cachefile[MAXFESTLEN]=""; 00307 int readcache=0; 00308 int writecache=0; 00309 int strln; 00310 int fdesc = -1; 00311 char buffer[16384]; 00312 int seekpos = 0; 00313 char *data; 00314 char *intstr; 00315 struct ast_config *cfg; 00316 char *newfestivalcommand; 00317 00318 if (ast_strlen_zero(vdata)) { 00319 ast_log(LOG_WARNING, "festival requires an argument (text)\n"); 00320 return -1; 00321 } 00322 00323 u = ast_module_user_add(chan); 00324 00325 cfg = ast_config_load(FESTIVAL_CONFIG); 00326 if (!cfg) { 00327 ast_log(LOG_WARNING, "No such configuration file %s\n", FESTIVAL_CONFIG); 00328 ast_module_user_remove(u); 00329 return -1; 00330 } 00331 if (!(host = ast_variable_retrieve(cfg, "general", "host"))) { 00332 host = "localhost"; 00333 } 00334 if (!(temp = ast_variable_retrieve(cfg, "general", "port"))) { 00335 port = 1314; 00336 } else { 00337 port = atoi(temp); 00338 } 00339 if (!(temp = ast_variable_retrieve(cfg, "general", "usecache"))) { 00340 usecache=0; 00341 } else { 00342 usecache = ast_true(temp); 00343 } 00344 if (!(cachedir = ast_variable_retrieve(cfg, "general", "cachedir"))) { 00345 cachedir = "/tmp/"; 00346 } 00347 if (!(festivalcommand = ast_variable_retrieve(cfg, "general", "festivalcommand"))) { 00348 festivalcommand = "(tts_textasterisk \"%s\" 'file)(quit)\n"; 00349 } else { /* This else parses the festivalcommand that we're sent from the config file for \n's, etc */ 00350 int i, j; 00351 newfestivalcommand = alloca(strlen(festivalcommand) + 1); 00352 00353 for (i = 0, j = 0; i < strlen(festivalcommand); i++) { 00354 if (festivalcommand[i] == '\\' && festivalcommand[i + 1] == 'n') { 00355 newfestivalcommand[j++] = '\n'; 00356 i++; 00357 } else if (festivalcommand[i] == '\\') { 00358 newfestivalcommand[j++] = festivalcommand[i + 1]; 00359 i++; 00360 } else 00361 newfestivalcommand[j++] = festivalcommand[i]; 00362 } 00363 newfestivalcommand[j] = '\0'; 00364 festivalcommand = newfestivalcommand; 00365 } 00366 00367 data = ast_strdupa(vdata); 00368 00369 intstr = strchr(data, '|'); 00370 if (intstr) { 00371 *intstr = '\0'; 00372 intstr++; 00373 if (!strcasecmp(intstr, "any")) 00374 intstr = AST_DIGIT_ANY; 00375 } 00376 00377 ast_log(LOG_DEBUG, "Text passed to festival server : %s\n",(char *)data); 00378 /* Connect to local festival server */ 00379 00380 fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 00381 00382 if (fd < 0) { 00383 ast_log(LOG_WARNING,"festival_client: can't get socket\n"); 00384 ast_config_destroy(cfg); 00385 ast_module_user_remove(u); 00386 return -1; 00387 } 00388 memset(&serv_addr, 0, sizeof(serv_addr)); 00389 if ((serv_addr.sin_addr.s_addr = inet_addr(host)) == -1) { 00390 /* its a name rather than an ipnum */ 00391 serverhost = ast_gethostbyname(host, &ahp); 00392 if (serverhost == (struct hostent *)0) { 00393 ast_log(LOG_WARNING,"festival_client: gethostbyname failed\n"); 00394 ast_config_destroy(cfg); 00395 ast_module_user_remove(u); 00396 return -1; 00397 } 00398 memmove(&serv_addr.sin_addr,serverhost->h_addr, serverhost->h_length); 00399 } 00400 serv_addr.sin_family = AF_INET; 00401 serv_addr.sin_port = htons(port); 00402 00403 if (connect(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) != 0) { 00404 ast_log(LOG_WARNING,"festival_client: connect to server failed\n"); 00405 ast_config_destroy(cfg); 00406 ast_module_user_remove(u); 00407 return -1; 00408 } 00409 00410 /* Compute MD5 sum of string */ 00411 MD5Init(&md5ctx); 00412 MD5Update(&md5ctx,(unsigned char const *)data,strlen(data)); 00413 MD5Final(MD5Res,&md5ctx); 00414 MD5Hex[0] = '\0'; 00415 00416 /* Convert to HEX and look if there is any matching file in the cache 00417 directory */ 00418 for (i=0;i<16;i++) { 00419 snprintf(koko, sizeof(koko), "%X",MD5Res[i]); 00420 strncat(MD5Hex, koko, sizeof(MD5Hex) - strlen(MD5Hex) - 1); 00421 } 00422 readcache=0; 00423 writecache=0; 00424 if (strlen(cachedir)+strlen(MD5Hex)+1<=MAXFESTLEN && (usecache==-1)) { 00425 snprintf(cachefile, sizeof(cachefile), "%s/%s", cachedir, MD5Hex); 00426 fdesc=open(cachefile,O_RDWR); 00427 if (fdesc==-1) { 00428 fdesc=open(cachefile,O_CREAT|O_RDWR,0777); 00429 if (fdesc!=-1) { 00430 writecache=1; 00431 strln=strlen((char *)data); 00432 ast_log(LOG_DEBUG,"line length : %d\n",strln); 00433 write(fdesc,&strln,sizeof(int)); 00434 write(fdesc,data,strln); 00435 seekpos=lseek(fdesc,0,SEEK_CUR); 00436 ast_log(LOG_DEBUG,"Seek position : %d\n",seekpos); 00437 } 00438 } else { 00439 read(fdesc,&strln,sizeof(int)); 00440 ast_log(LOG_DEBUG,"Cache file exists, strln=%d, strlen=%d\n",strln,(int)strlen((char *)data)); 00441 if (strlen((char *)data)==strln) { 00442 ast_log(LOG_DEBUG,"Size OK\n"); 00443 read(fdesc,&bigstring,strln); 00444 bigstring[strln] = 0; 00445 if (strcmp(bigstring,data)==0) { 00446 readcache=1; 00447 } else { 00448 ast_log(LOG_WARNING,"Strings do not match\n"); 00449 } 00450 } else { 00451 ast_log(LOG_WARNING,"Size mismatch\n"); 00452 } 00453 } 00454 } 00455 00456 if (readcache==1) { 00457 close(fd); 00458 fd=fdesc; 00459 ast_log(LOG_DEBUG,"Reading from cache...\n"); 00460 } else { 00461 ast_log(LOG_DEBUG,"Passing text to festival...\n"); 00462 fs=fdopen(dup(fd),"wb"); 00463 fprintf(fs,festivalcommand,(char *)data); 00464 fflush(fs); 00465 fclose(fs); 00466 } 00467 00468 /* Write to cache and then pass it down */ 00469 if (writecache==1) { 00470 ast_log(LOG_DEBUG,"Writing result to cache...\n"); 00471 while ((strln=read(fd,buffer,16384))!=0) { 00472 write(fdesc,buffer,strln); 00473 } 00474 close(fd); 00475 close(fdesc); 00476 fd=open(cachefile,O_RDWR); 00477 lseek(fd,seekpos,SEEK_SET); 00478 } 00479 00480 ast_log(LOG_DEBUG,"Passing data to channel...\n"); 00481 00482 /* Read back info from server */ 00483 /* This assumes only one waveform will come back, also LP is unlikely */ 00484 wave = 0; 00485 do { 00486 int read_data; 00487 for (n=0; n < 3; ) 00488 { 00489 read_data = read(fd,ack+n,3-n); 00490 /* this avoids falling in infinite loop 00491 * in case that festival server goes down 00492 * */ 00493 if ( read_data == -1 ) 00494 { 00495 ast_log(LOG_WARNING,"Unable to read from cache/festival fd\n"); 00496 close(fd); 00497 ast_config_destroy(cfg); 00498 ast_module_user_remove(u); 00499 return -1; 00500 } 00501 n += read_data; 00502 } 00503 ack[3] = '\0'; 00504 if (strcmp(ack,"WV\n") == 0) { /* receive a waveform */ 00505 ast_log(LOG_DEBUG,"Festival WV command\n"); 00506 waveform = socket_receive_file_to_buff(fd,&filesize); 00507 res = send_waveform_to_channel(chan,waveform,filesize, intstr); 00508 free(waveform); 00509 break; 00510 } 00511 else if (strcmp(ack,"LP\n") == 0) { /* receive an s-expr */ 00512 ast_log(LOG_DEBUG,"Festival LP command\n"); 00513 waveform = socket_receive_file_to_buff(fd,&filesize); 00514 waveform[filesize]='\0'; 00515 ast_log(LOG_WARNING,"Festival returned LP : %s\n",waveform); 00516 free(waveform); 00517 } else if (strcmp(ack,"ER\n") == 0) { /* server got an error */ 00518 ast_log(LOG_WARNING,"Festival returned ER\n"); 00519 res=-1; 00520 break; 00521 } 00522 } while (strcmp(ack,"OK\n") != 0); 00523 close(fd); 00524 ast_config_destroy(cfg); 00525 ast_module_user_remove(u); 00526 return res; 00527 00528 }
static int load_module | ( | void | ) | [static] |
Definition at line 541 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.
00542 { 00543 struct ast_config *cfg = ast_config_load(FESTIVAL_CONFIG); 00544 if (!cfg) { 00545 ast_log(LOG_WARNING, "No such configuration file %s\n", FESTIVAL_CONFIG); 00546 return AST_MODULE_LOAD_DECLINE; 00547 } 00548 ast_config_destroy(cfg); 00549 return ast_register_application(app, festival_exec, synopsis, descrip); 00550 }
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 00183 if (pipe(fds)) { 00184 ast_log(LOG_WARNING, "Unable to create pipe\n"); 00185 return -1; 00186 } 00187 00188 /* Answer if it's not already going */ 00189 if (chan->_state != AST_STATE_UP) 00190 ast_answer(chan); 00191 ast_stopstream(chan); 00192 ast_indicate(chan, -1); 00193 00194 owriteformat = chan->writeformat; 00195 res = ast_set_write_format(chan, AST_FORMAT_SLINEAR); 00196 if (res < 0) { 00197 ast_log(LOG_WARNING, "Unable to set write format to signed linear\n"); 00198 return -1; 00199 } 00200 00201 res=send_waveform_to_fd(waveform,length,fds[1]); 00202 if (res >= 0) { 00203 pid = res; 00204 /* Order is important -- there's almost always going to be mp3... we want to prioritize the 00205 user */ 00206 for (;;) { 00207 ms = 1000; 00208 res = ast_waitfor(chan, ms); 00209 if (res < 1) { 00210 res = -1; 00211 break; 00212 } 00213 f = ast_read(chan); 00214 if (!f) { 00215 ast_log(LOG_WARNING, "Null frame == hangup() detected\n"); 00216 res = -1; 00217 break; 00218 } 00219 if (f->frametype == AST_FRAME_DTMF) { 00220 ast_log(LOG_DEBUG, "User pressed a key\n"); 00221 if (intkeys && strchr(intkeys, f->subclass)) { 00222 res = f->subclass; 00223 ast_frfree(f); 00224 break; 00225 } 00226 } 00227 if (f->frametype == AST_FRAME_VOICE) { 00228 /* Treat as a generator */ 00229 needed = f->samples * 2; 00230 if (needed > sizeof(myf.frdata)) { 00231 ast_log(LOG_WARNING, "Only able to deliver %d of %d requested samples\n", 00232 (int)sizeof(myf.frdata) / 2, needed/2); 00233 needed = sizeof(myf.frdata); 00234 } 00235 res = read(fds[0], myf.frdata, needed); 00236 if (res > 0) { 00237 myf.f.frametype = AST_FRAME_VOICE; 00238 myf.f.subclass = AST_FORMAT_SLINEAR; 00239 myf.f.datalen = res; 00240 myf.f.samples = res / 2; 00241 myf.f.mallocd = 0; 00242 myf.f.offset = AST_FRIENDLY_OFFSET; 00243 myf.f.src = __PRETTY_FUNCTION__; 00244 myf.f.data = myf.frdata; 00245 if (ast_write(chan, &myf.f) < 0) { 00246 res = -1; 00247 ast_frfree(f); 00248 break; 00249 } 00250 if (res < needed) { /* last frame */ 00251 ast_log(LOG_DEBUG, "Last frame\n"); 00252 res=0; 00253 ast_frfree(f); 00254 break; 00255 } 00256 } else { 00257 ast_log(LOG_DEBUG, "No more waveform\n"); 00258 res = 0; 00259 } 00260 } 00261 ast_frfree(f); 00262 } 00263 } 00264 close(fds[0]); 00265 close(fds[1]); 00266 00267 /* if (pid > -1) */ 00268 /* kill(pid, SIGKILL); */ 00269 if (!res && owriteformat) 00270 ast_set_write_format(chan, owriteformat); 00271 return res; 00272 }
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 530 of file app_festival.c.
References ast_module_user_hangup_all, and ast_unregister_application().
00531 { 00532 int res; 00533 00534 res = ast_unregister_application(app); 00535 00536 ast_module_user_hangup_all(); 00537 00538 return res; 00539 }
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 64 of file app_festival.c.