#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 <errno.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 | |
static void | __reg_module (void) |
static void | __unreg_module (void) |
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 struct ast_module_info | __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_BUILDSUM, .description = "Simple Festival Interface" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "f450f61f60e761b3aa089ebed76ca8a5" , .load = load_module, .unload = unload_module, } |
static char * | app = "Festival" |
static const struct ast_module_info * | ast_module_info = &__mod_info |
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 278 of file app_festival.c.
static void __reg_module | ( | void | ) | [static] |
Definition at line 566 of file app_festival.c.
static void __unreg_module | ( | void | ) | [static] |
Definition at line 566 of file app_festival.c.
static int festival_exec | ( | struct ast_channel * | chan, | |
void * | vdata | |||
) | [static] |
Definition at line 284 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(), errno, 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().
00285 { 00286 int usecache; 00287 int res=0; 00288 struct ast_module_user *u; 00289 struct sockaddr_in serv_addr; 00290 struct hostent *serverhost; 00291 struct ast_hostent ahp; 00292 int fd; 00293 FILE *fs; 00294 const char *host; 00295 const char *cachedir; 00296 const char *temp; 00297 const char *festivalcommand; 00298 int port=1314; 00299 int n; 00300 char ack[4]; 00301 char *waveform; 00302 int filesize; 00303 int wave; 00304 char bigstring[MAXFESTLEN]; 00305 int i; 00306 struct MD5Context md5ctx; 00307 unsigned char MD5Res[16]; 00308 char MD5Hex[33] = ""; 00309 char koko[4] = ""; 00310 char cachefile[MAXFESTLEN]=""; 00311 int readcache=0; 00312 int writecache=0; 00313 int strln; 00314 int fdesc = -1; 00315 char buffer[16384]; 00316 int seekpos = 0; 00317 char *data; 00318 char *intstr; 00319 struct ast_config *cfg; 00320 char *newfestivalcommand; 00321 00322 if (ast_strlen_zero(vdata)) { 00323 ast_log(LOG_WARNING, "festival requires an argument (text)\n"); 00324 return -1; 00325 } 00326 00327 u = ast_module_user_add(chan); 00328 00329 cfg = ast_config_load(FESTIVAL_CONFIG); 00330 if (!cfg) { 00331 ast_log(LOG_WARNING, "No such configuration file %s\n", FESTIVAL_CONFIG); 00332 ast_module_user_remove(u); 00333 return -1; 00334 } 00335 if (!(host = ast_variable_retrieve(cfg, "general", "host"))) { 00336 host = "localhost"; 00337 } 00338 if (!(temp = ast_variable_retrieve(cfg, "general", "port"))) { 00339 port = 1314; 00340 } else { 00341 port = atoi(temp); 00342 } 00343 if (!(temp = ast_variable_retrieve(cfg, "general", "usecache"))) { 00344 usecache=0; 00345 } else { 00346 usecache = ast_true(temp); 00347 } 00348 if (!(cachedir = ast_variable_retrieve(cfg, "general", "cachedir"))) { 00349 cachedir = "/tmp/"; 00350 } 00351 if (!(festivalcommand = ast_variable_retrieve(cfg, "general", "festivalcommand"))) { 00352 festivalcommand = "(tts_textasterisk \"%s\" 'file)(quit)\n"; 00353 } else { /* This else parses the festivalcommand that we're sent from the config file for \n's, etc */ 00354 int i, j; 00355 newfestivalcommand = alloca(strlen(festivalcommand) + 1); 00356 00357 for (i = 0, j = 0; i < strlen(festivalcommand); i++) { 00358 if (festivalcommand[i] == '\\' && festivalcommand[i + 1] == 'n') { 00359 newfestivalcommand[j++] = '\n'; 00360 i++; 00361 } else if (festivalcommand[i] == '\\') { 00362 newfestivalcommand[j++] = festivalcommand[i + 1]; 00363 i++; 00364 } else 00365 newfestivalcommand[j++] = festivalcommand[i]; 00366 } 00367 newfestivalcommand[j] = '\0'; 00368 festivalcommand = newfestivalcommand; 00369 } 00370 00371 data = ast_strdupa(vdata); 00372 00373 intstr = strchr(data, '|'); 00374 if (intstr) { 00375 *intstr = '\0'; 00376 intstr++; 00377 if (!strcasecmp(intstr, "any")) 00378 intstr = AST_DIGIT_ANY; 00379 } 00380 00381 ast_log(LOG_DEBUG, "Text passed to festival server : %s\n",(char *)data); 00382 /* Connect to local festival server */ 00383 00384 fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 00385 00386 if (fd < 0) { 00387 ast_log(LOG_WARNING,"festival_client: can't get socket\n"); 00388 ast_config_destroy(cfg); 00389 ast_module_user_remove(u); 00390 return -1; 00391 } 00392 memset(&serv_addr, 0, sizeof(serv_addr)); 00393 if ((serv_addr.sin_addr.s_addr = inet_addr(host)) == -1) { 00394 /* its a name rather than an ipnum */ 00395 serverhost = ast_gethostbyname(host, &ahp); 00396 if (serverhost == (struct hostent *)0) { 00397 ast_log(LOG_WARNING,"festival_client: gethostbyname failed\n"); 00398 ast_config_destroy(cfg); 00399 ast_module_user_remove(u); 00400 return -1; 00401 } 00402 memmove(&serv_addr.sin_addr,serverhost->h_addr, serverhost->h_length); 00403 } 00404 serv_addr.sin_family = AF_INET; 00405 serv_addr.sin_port = htons(port); 00406 00407 if (connect(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) != 0) { 00408 ast_log(LOG_WARNING,"festival_client: connect to server failed\n"); 00409 ast_config_destroy(cfg); 00410 ast_module_user_remove(u); 00411 return -1; 00412 } 00413 00414 /* Compute MD5 sum of string */ 00415 MD5Init(&md5ctx); 00416 MD5Update(&md5ctx,(unsigned char const *)data,strlen(data)); 00417 MD5Final(MD5Res,&md5ctx); 00418 MD5Hex[0] = '\0'; 00419 00420 /* Convert to HEX and look if there is any matching file in the cache 00421 directory */ 00422 for (i=0;i<16;i++) { 00423 snprintf(koko, sizeof(koko), "%X",MD5Res[i]); 00424 strncat(MD5Hex, koko, sizeof(MD5Hex) - strlen(MD5Hex) - 1); 00425 } 00426 readcache=0; 00427 writecache=0; 00428 if (strlen(cachedir)+strlen(MD5Hex)+1<=MAXFESTLEN && (usecache==-1)) { 00429 snprintf(cachefile, sizeof(cachefile), "%s/%s", cachedir, MD5Hex); 00430 fdesc=open(cachefile,O_RDWR); 00431 if (fdesc==-1) { 00432 fdesc=open(cachefile,O_CREAT|O_RDWR,0777); 00433 if (fdesc!=-1) { 00434 writecache=1; 00435 strln=strlen((char *)data); 00436 ast_log(LOG_DEBUG,"line length : %d\n",strln); 00437 if (write(fdesc,&strln,sizeof(int)) < 0) { 00438 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno)); 00439 } 00440 if (write(fdesc,data,strln) < 0) { 00441 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno)); 00442 } 00443 seekpos=lseek(fdesc,0,SEEK_CUR); 00444 ast_log(LOG_DEBUG,"Seek position : %d\n",seekpos); 00445 } 00446 } else { 00447 if (read(fdesc,&strln,sizeof(int)) != sizeof(int)) { 00448 ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno)); 00449 } 00450 ast_log(LOG_DEBUG,"Cache file exists, strln=%d, strlen=%d\n",strln,(int)strlen((char *)data)); 00451 if (strlen((char *)data)==strln) { 00452 ast_log(LOG_DEBUG,"Size OK\n"); 00453 if (read(fdesc,&bigstring,strln) != strln) { 00454 ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno)); 00455 } 00456 bigstring[strln] = 0; 00457 if (strcmp(bigstring,data)==0) { 00458 readcache=1; 00459 } else { 00460 ast_log(LOG_WARNING,"Strings do not match\n"); 00461 } 00462 } else { 00463 ast_log(LOG_WARNING,"Size mismatch\n"); 00464 } 00465 } 00466 } 00467 00468 if (readcache==1) { 00469 close(fd); 00470 fd=fdesc; 00471 ast_log(LOG_DEBUG,"Reading from cache...\n"); 00472 } else { 00473 ast_log(LOG_DEBUG,"Passing text to festival...\n"); 00474 fs=fdopen(dup(fd),"wb"); 00475 fprintf(fs,festivalcommand,(char *)data); 00476 fflush(fs); 00477 fclose(fs); 00478 } 00479 00480 /* Write to cache and then pass it down */ 00481 if (writecache==1) { 00482 ast_log(LOG_DEBUG,"Writing result to cache...\n"); 00483 while ((strln=read(fd,buffer,16384))!=0) { 00484 if (write(fdesc,buffer,strln) < 0) { 00485 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno)); 00486 } 00487 } 00488 close(fd); 00489 close(fdesc); 00490 fd=open(cachefile,O_RDWR); 00491 lseek(fd,seekpos,SEEK_SET); 00492 } 00493 00494 ast_log(LOG_DEBUG,"Passing data to channel...\n"); 00495 00496 /* Read back info from server */ 00497 /* This assumes only one waveform will come back, also LP is unlikely */ 00498 wave = 0; 00499 do { 00500 int read_data; 00501 for (n=0; n < 3; ) 00502 { 00503 read_data = read(fd,ack+n,3-n); 00504 /* this avoids falling in infinite loop 00505 * in case that festival server goes down 00506 * */ 00507 if ( read_data == -1 ) 00508 { 00509 ast_log(LOG_WARNING,"Unable to read from cache/festival fd\n"); 00510 close(fd); 00511 ast_config_destroy(cfg); 00512 ast_module_user_remove(u); 00513 return -1; 00514 } 00515 n += read_data; 00516 } 00517 ack[3] = '\0'; 00518 if (strcmp(ack,"WV\n") == 0) { /* receive a waveform */ 00519 ast_log(LOG_DEBUG,"Festival WV command\n"); 00520 waveform = socket_receive_file_to_buff(fd,&filesize); 00521 res = send_waveform_to_channel(chan,waveform,filesize, intstr); 00522 free(waveform); 00523 break; 00524 } 00525 else if (strcmp(ack,"LP\n") == 0) { /* receive an s-expr */ 00526 ast_log(LOG_DEBUG,"Festival LP command\n"); 00527 waveform = socket_receive_file_to_buff(fd,&filesize); 00528 waveform[filesize]='\0'; 00529 ast_log(LOG_WARNING,"Festival returned LP : %s\n",waveform); 00530 free(waveform); 00531 } else if (strcmp(ack,"ER\n") == 0) { /* server got an error */ 00532 ast_log(LOG_WARNING,"Festival returned ER\n"); 00533 res=-1; 00534 break; 00535 } 00536 } while (strcmp(ack,"OK\n") != 0); 00537 close(fd); 00538 ast_config_destroy(cfg); 00539 ast_module_user_remove(u); 00540 return res; 00541 00542 }
static int load_module | ( | void | ) | [static] |
Definition at line 555 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.
00556 { 00557 struct ast_config *cfg = ast_config_load(FESTIVAL_CONFIG); 00558 if (!cfg) { 00559 ast_log(LOG_WARNING, "No such configuration file %s\n", FESTIVAL_CONFIG); 00560 return AST_MODULE_LOAD_DECLINE; 00561 } 00562 ast_config_destroy(cfg); 00563 return ast_register_application(app, festival_exec, synopsis, descrip); 00564 }
static int send_waveform_to_channel | ( | struct ast_channel * | chan, | |
char * | waveform, | |||
int | length, | |||
char * | intkeys | |||
) | [static] |
Definition at line 172 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().
00172 { 00173 int res=0; 00174 int fds[2]; 00175 int ms = -1; 00176 int pid = -1; 00177 int needed = 0; 00178 int owriteformat; 00179 struct ast_frame *f; 00180 struct myframe { 00181 struct ast_frame f; 00182 char offset[AST_FRIENDLY_OFFSET]; 00183 char frdata[2048]; 00184 } myf = { 00185 .f = { 0, }, 00186 }; 00187 00188 if (pipe(fds)) { 00189 ast_log(LOG_WARNING, "Unable to create pipe\n"); 00190 return -1; 00191 } 00192 00193 /* Answer if it's not already going */ 00194 if (chan->_state != AST_STATE_UP) 00195 ast_answer(chan); 00196 ast_stopstream(chan); 00197 ast_indicate(chan, -1); 00198 00199 owriteformat = chan->writeformat; 00200 res = ast_set_write_format(chan, AST_FORMAT_SLINEAR); 00201 if (res < 0) { 00202 ast_log(LOG_WARNING, "Unable to set write format to signed linear\n"); 00203 return -1; 00204 } 00205 00206 res=send_waveform_to_fd(waveform,length,fds[1]); 00207 if (res >= 0) { 00208 pid = res; 00209 /* Order is important -- there's almost always going to be mp3... we want to prioritize the 00210 user */ 00211 for (;;) { 00212 ms = 1000; 00213 res = ast_waitfor(chan, ms); 00214 if (res < 1) { 00215 res = -1; 00216 break; 00217 } 00218 f = ast_read(chan); 00219 if (!f) { 00220 ast_log(LOG_WARNING, "Null frame == hangup() detected\n"); 00221 res = -1; 00222 break; 00223 } 00224 if (f->frametype == AST_FRAME_DTMF) { 00225 ast_log(LOG_DEBUG, "User pressed a key\n"); 00226 if (intkeys && strchr(intkeys, f->subclass)) { 00227 res = f->subclass; 00228 ast_frfree(f); 00229 break; 00230 } 00231 } 00232 if (f->frametype == AST_FRAME_VOICE) { 00233 /* Treat as a generator */ 00234 needed = f->samples * 2; 00235 if (needed > sizeof(myf.frdata)) { 00236 ast_log(LOG_WARNING, "Only able to deliver %d of %d requested samples\n", 00237 (int)sizeof(myf.frdata) / 2, needed/2); 00238 needed = sizeof(myf.frdata); 00239 } 00240 res = read(fds[0], myf.frdata, needed); 00241 if (res > 0) { 00242 myf.f.frametype = AST_FRAME_VOICE; 00243 myf.f.subclass = AST_FORMAT_SLINEAR; 00244 myf.f.datalen = res; 00245 myf.f.samples = res / 2; 00246 myf.f.offset = AST_FRIENDLY_OFFSET; 00247 myf.f.src = __PRETTY_FUNCTION__; 00248 myf.f.data = myf.frdata; 00249 if (ast_write(chan, &myf.f) < 0) { 00250 res = -1; 00251 ast_frfree(f); 00252 break; 00253 } 00254 if (res < needed) { /* last frame */ 00255 ast_log(LOG_DEBUG, "Last frame\n"); 00256 res=0; 00257 ast_frfree(f); 00258 break; 00259 } 00260 } else { 00261 ast_log(LOG_DEBUG, "No more waveform\n"); 00262 res = 0; 00263 } 00264 } 00265 ast_frfree(f); 00266 } 00267 } 00268 close(fds[0]); 00269 close(fds[1]); 00270 00271 /* if (pid > -1) */ 00272 /* kill(pid, SIGKILL); */ 00273 if (!res && owriteformat) 00274 ast_set_write_format(chan, owriteformat); 00275 return res; 00276 }
static int send_waveform_to_fd | ( | char * | waveform, | |
int | length, | |||
int | fd | |||
) | [static] |
Definition at line 127 of file app_festival.c.
References ast_log(), ast_opt_high_priority, ast_set_priority(), errno, and LOG_WARNING.
Referenced by send_waveform_to_channel().
00127 { 00128 00129 int res; 00130 int x; 00131 #ifdef __PPC__ 00132 char c; 00133 #endif 00134 sigset_t fullset, oldset; 00135 00136 sigfillset(&fullset); 00137 pthread_sigmask(SIG_BLOCK, &fullset, &oldset); 00138 00139 res = fork(); 00140 if (res < 0) 00141 ast_log(LOG_WARNING, "Fork failed\n"); 00142 if (res) { 00143 pthread_sigmask(SIG_SETMASK, &oldset, NULL); 00144 return res; 00145 } 00146 for (x=0;x<256;x++) { 00147 if (x != fd) 00148 close(x); 00149 } 00150 if (ast_opt_high_priority) 00151 ast_set_priority(0); 00152 signal(SIGPIPE, SIG_DFL); 00153 pthread_sigmask(SIG_UNBLOCK, &fullset, NULL); 00154 /*IAS */ 00155 #ifdef __PPC__ 00156 for( x=0; x<length; x+=2) 00157 { 00158 c = *(waveform+x+1); 00159 *(waveform+x+1)=*(waveform+x); 00160 *(waveform+x)=c; 00161 } 00162 #endif 00163 00164 if (write(fd,waveform,length) < 0) { 00165 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno)); 00166 } 00167 close(fd); 00168 exit(0); 00169 }
static char* socket_receive_file_to_buff | ( | int | fd, | |
int * | size | |||
) | [static] |
Definition at line 73 of file app_festival.c.
References ast_malloc, and ast_realloc.
Referenced by festival_exec().
00074 { 00075 /* Receive file (probably a waveform file) from socket using */ 00076 /* Festival key stuff technique, but long winded I know, sorry */ 00077 /* but will receive any file without closeing the stream or */ 00078 /* using OOB data */ 00079 static char *file_stuff_key = "ft_StUfF_key"; /* must == Festival's key */ 00080 char *buff; 00081 int bufflen; 00082 int n,k,i; 00083 char c; 00084 00085 bufflen = 1024; 00086 if (!(buff = ast_malloc(bufflen))) 00087 { 00088 /* TODO: Handle memory allocation failure */ 00089 } 00090 *size=0; 00091 00092 for (k=0; file_stuff_key[k] != '\0';) 00093 { 00094 n = read(fd,&c,1); 00095 if (n==0) break; /* hit stream eof before end of file */ 00096 if ((*size)+k+1 >= bufflen) 00097 { /* +1 so you can add a NULL if you want */ 00098 bufflen += bufflen/4; 00099 if (!(buff = ast_realloc(buff, bufflen))) 00100 { 00101 /* TODO: Handle memory allocation failure */ 00102 } 00103 } 00104 if (file_stuff_key[k] == c) 00105 k++; 00106 else if ((c == 'X') && (file_stuff_key[k+1] == '\0')) 00107 { /* It looked like the key but wasn't */ 00108 for (i=0; i < k; i++,(*size)++) 00109 buff[*size] = file_stuff_key[i]; 00110 k=0; 00111 /* omit the stuffed 'X' */ 00112 } 00113 else 00114 { 00115 for (i=0; i < k; i++,(*size)++) 00116 buff[*size] = file_stuff_key[i]; 00117 k=0; 00118 buff[*size] = c; 00119 (*size)++; 00120 } 00121 00122 } 00123 00124 return buff; 00125 }
static int unload_module | ( | void | ) | [static] |
Definition at line 544 of file app_festival.c.
References ast_module_user_hangup_all, and ast_unregister_application().
00545 { 00546 int res; 00547 00548 res = ast_unregister_application(app); 00549 00550 ast_module_user_hangup_all(); 00551 00552 return res; 00553 }
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_BUILDSUM, .description = "Simple Festival Interface" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "f450f61f60e761b3aa089ebed76ca8a5" , .load = load_module, .unload = unload_module, } [static] |
Definition at line 566 of file app_festival.c.
char* app = "Festival" [static] |
Definition at line 63 of file app_festival.c.
const struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 566 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 67 of file app_festival.c.
Definition at line 65 of file app_festival.c.