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