#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"
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 275 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 281 of file app_festival.c.
References 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, 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().
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 closeing 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 }
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.