00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030 #include "asterisk.h"
00031
00032 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 208116 $")
00033
00034 #include <sys/socket.h>
00035 #include <netdb.h>
00036 #include <netinet/in.h>
00037 #include <arpa/inet.h>
00038 #include <signal.h>
00039 #include <fcntl.h>
00040 #include <ctype.h>
00041 #include <errno.h>
00042
00043 #include "asterisk/file.h"
00044 #include "asterisk/channel.h"
00045 #include "asterisk/pbx.h"
00046 #include "asterisk/module.h"
00047 #include "asterisk/md5.h"
00048 #include "asterisk/config.h"
00049 #include "asterisk/utils.h"
00050 #include "asterisk/lock.h"
00051 #include "asterisk/app.h"
00052
00053 #define FESTIVAL_CONFIG "festival.conf"
00054 #define MAXLEN 180
00055 #define MAXFESTLEN 2048
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074 static char *app = "Festival";
00075
00076 static char *socket_receive_file_to_buff(int fd, int *size)
00077 {
00078
00079
00080
00081
00082
00083 static char *file_stuff_key = "ft_StUfF_key";
00084 char *buff, *tmp;
00085 int bufflen;
00086 int n,k,i;
00087 char c;
00088
00089 bufflen = 1024;
00090 if (!(buff = ast_malloc(bufflen)))
00091 return NULL;
00092 *size = 0;
00093
00094 for (k = 0; file_stuff_key[k] != '\0';) {
00095 n = read(fd, &c, 1);
00096 if (n == 0)
00097 break;
00098 if ((*size) + k + 1 >= bufflen) {
00099
00100 bufflen += bufflen / 4;
00101 if (!(tmp = ast_realloc(buff, bufflen))) {
00102 ast_free(buff);
00103 return NULL;
00104 }
00105 buff = tmp;
00106 }
00107 if (file_stuff_key[k] == c)
00108 k++;
00109 else if ((c == 'X') && (file_stuff_key[k+1] == '\0')) {
00110
00111 for (i = 0; i < k; i++, (*size)++)
00112 buff[*size] = file_stuff_key[i];
00113 k = 0;
00114
00115 } else {
00116 for (i = 0; i < k; i++, (*size)++)
00117 buff[*size] = file_stuff_key[i];
00118 k = 0;
00119 buff[*size] = c;
00120 (*size)++;
00121 }
00122 }
00123
00124 return buff;
00125 }
00126
00127 static int send_waveform_to_fd(char *waveform, int length, int fd)
00128 {
00129 int res;
00130 #ifdef __PPC__
00131 int x;
00132 char c;
00133 #endif
00134
00135 res = ast_safe_fork(0);
00136 if (res < 0)
00137 ast_log(LOG_WARNING, "Fork failed\n");
00138 if (res) {
00139 return res;
00140 }
00141 dup2(fd, 0);
00142 ast_close_fds_above_n(0);
00143 if (ast_opt_high_priority)
00144 ast_set_priority(0);
00145 #ifdef __PPC__
00146 for (x = 0; x < length; x += 2) {
00147 c = *(waveform + x + 1);
00148 *(waveform + x + 1) = *(waveform + x);
00149 *(waveform + x) = c;
00150 }
00151 #endif
00152
00153 if (write(fd, waveform, length) < 0) {
00154 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
00155 }
00156
00157 close(fd);
00158 exit(0);
00159 }
00160
00161 static int send_waveform_to_channel(struct ast_channel *chan, char *waveform, int length, char *intkeys)
00162 {
00163 int res = 0;
00164 int fds[2];
00165 int pid = -1;
00166 int needed = 0;
00167 int owriteformat;
00168 struct ast_frame *f;
00169 struct myframe {
00170 struct ast_frame f;
00171 char offset[AST_FRIENDLY_OFFSET];
00172 char frdata[2048];
00173 } myf = {
00174 .f = { 0, },
00175 };
00176
00177 if (pipe(fds)) {
00178 ast_log(LOG_WARNING, "Unable to create pipe\n");
00179 return -1;
00180 }
00181
00182
00183 if (chan->_state != AST_STATE_UP)
00184 ast_answer(chan);
00185 ast_stopstream(chan);
00186 ast_indicate(chan, -1);
00187
00188 owriteformat = chan->writeformat;
00189 res = ast_set_write_format(chan, AST_FORMAT_SLINEAR);
00190 if (res < 0) {
00191 ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
00192 return -1;
00193 }
00194
00195 res = send_waveform_to_fd(waveform, length, fds[1]);
00196 if (res >= 0) {
00197 pid = res;
00198
00199
00200 for (;;) {
00201 res = ast_waitfor(chan, 1000);
00202 if (res < 1) {
00203 res = -1;
00204 break;
00205 }
00206 f = ast_read(chan);
00207 if (!f) {
00208 ast_log(LOG_WARNING, "Null frame == hangup() detected\n");
00209 res = -1;
00210 break;
00211 }
00212 if (f->frametype == AST_FRAME_DTMF) {
00213 ast_debug(1, "User pressed a key\n");
00214 if (intkeys && strchr(intkeys, f->subclass)) {
00215 res = f->subclass;
00216 ast_frfree(f);
00217 break;
00218 }
00219 }
00220 if (f->frametype == AST_FRAME_VOICE) {
00221
00222 needed = f->samples * 2;
00223 if (needed > sizeof(myf.frdata)) {
00224 ast_log(LOG_WARNING, "Only able to deliver %d of %d requested samples\n",
00225 (int)sizeof(myf.frdata) / 2, needed/2);
00226 needed = sizeof(myf.frdata);
00227 }
00228 res = read(fds[0], myf.frdata, needed);
00229 if (res > 0) {
00230 myf.f.frametype = AST_FRAME_VOICE;
00231 myf.f.subclass = AST_FORMAT_SLINEAR;
00232 myf.f.datalen = res;
00233 myf.f.samples = res / 2;
00234 myf.f.offset = AST_FRIENDLY_OFFSET;
00235 myf.f.src = __PRETTY_FUNCTION__;
00236 myf.f.data.ptr = myf.frdata;
00237 if (ast_write(chan, &myf.f) < 0) {
00238 res = -1;
00239 ast_frfree(f);
00240 break;
00241 }
00242 if (res < needed) {
00243 ast_debug(1, "Last frame\n");
00244 res = 0;
00245 ast_frfree(f);
00246 break;
00247 }
00248 } else {
00249 ast_debug(1, "No more waveform\n");
00250 res = 0;
00251 }
00252 }
00253 ast_frfree(f);
00254 }
00255 }
00256 close(fds[0]);
00257 close(fds[1]);
00258
00259 #if 0
00260 if (pid > -1)
00261 kill(pid, SIGKILL);
00262 #endif
00263 if (!res && owriteformat)
00264 ast_set_write_format(chan, owriteformat);
00265 return res;
00266 }
00267
00268 static int festival_exec(struct ast_channel *chan, void *vdata)
00269 {
00270 int usecache;
00271 int res = 0;
00272 struct sockaddr_in serv_addr;
00273 struct hostent *serverhost;
00274 struct ast_hostent ahp;
00275 int fd;
00276 FILE *fs;
00277 const char *host;
00278 const char *cachedir;
00279 const char *temp;
00280 const char *festivalcommand;
00281 int port = 1314;
00282 int n;
00283 char ack[4];
00284 char *waveform;
00285 int filesize;
00286 int wave;
00287 char bigstring[MAXFESTLEN];
00288 int i;
00289 struct MD5Context md5ctx;
00290 unsigned char MD5Res[16];
00291 char MD5Hex[33] = "";
00292 char koko[4] = "";
00293 char cachefile[MAXFESTLEN]="";
00294 int readcache = 0;
00295 int writecache = 0;
00296 int strln;
00297 int fdesc = -1;
00298 char buffer[16384];
00299 int seekpos = 0;
00300 char *data;
00301 struct ast_config *cfg;
00302 char *newfestivalcommand;
00303 struct ast_flags config_flags = { 0 };
00304 AST_DECLARE_APP_ARGS(args,
00305 AST_APP_ARG(text);
00306 AST_APP_ARG(interrupt);
00307 );
00308
00309 if (ast_strlen_zero(vdata)) {
00310 ast_log(LOG_WARNING, "festival requires an argument (text)\n");
00311 return -1;
00312 }
00313
00314 cfg = ast_config_load(FESTIVAL_CONFIG, config_flags);
00315 if (!cfg) {
00316 ast_log(LOG_WARNING, "No such configuration file %s\n", FESTIVAL_CONFIG);
00317 return -1;
00318 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
00319 ast_log(LOG_ERROR, "Config file " FESTIVAL_CONFIG " is in an invalid format. Aborting.\n");
00320 return -1;
00321 }
00322
00323 if (!(host = ast_variable_retrieve(cfg, "general", "host"))) {
00324 host = "localhost";
00325 }
00326 if (!(temp = ast_variable_retrieve(cfg, "general", "port"))) {
00327 port = 1314;
00328 } else {
00329 port = atoi(temp);
00330 }
00331 if (!(temp = ast_variable_retrieve(cfg, "general", "usecache"))) {
00332 usecache = 0;
00333 } else {
00334 usecache = ast_true(temp);
00335 }
00336 if (!(cachedir = ast_variable_retrieve(cfg, "general", "cachedir"))) {
00337 cachedir = "/tmp/";
00338 }
00339
00340 data = ast_strdupa(vdata);
00341 AST_STANDARD_APP_ARGS(args, data);
00342
00343 if (!(festivalcommand = ast_variable_retrieve(cfg, "general", "festivalcommand"))) {
00344 const char *startcmd = "(tts_textasterisk \"";
00345 const char *endcmd = "\" 'file)(quit)\n";
00346
00347 strln = strlen(startcmd) + strlen(args.text) + strlen(endcmd) + 1;
00348 newfestivalcommand = alloca(strln);
00349 snprintf(newfestivalcommand, strln, "%s%s%s", startcmd, args.text, endcmd);
00350 festivalcommand = newfestivalcommand;
00351 } else {
00352 int x, j;
00353 newfestivalcommand = alloca(strlen(festivalcommand) + strlen(args.text) + 1);
00354
00355 for (x = 0, j = 0; x < strlen(festivalcommand); x++) {
00356 if (festivalcommand[x] == '\\' && festivalcommand[x + 1] == 'n') {
00357 newfestivalcommand[j++] = '\n';
00358 x++;
00359 } else if (festivalcommand[x] == '\\') {
00360 newfestivalcommand[j++] = festivalcommand[x + 1];
00361 x++;
00362 } else if (festivalcommand[x] == '%' && festivalcommand[x + 1] == 's') {
00363 sprintf(&newfestivalcommand[j], "%s", args.text);
00364 j += strlen(args.text);
00365 x++;
00366 } else
00367 newfestivalcommand[j++] = festivalcommand[x];
00368 }
00369 newfestivalcommand[j] = '\0';
00370 festivalcommand = newfestivalcommand;
00371 }
00372
00373 if (args.interrupt && !strcasecmp(args.interrupt, "any"))
00374 args.interrupt = AST_DIGIT_ANY;
00375
00376 ast_debug(1, "Text passed to festival server : %s\n", args.text);
00377
00378
00379 fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
00380
00381 if (fd < 0) {
00382 ast_log(LOG_WARNING, "festival_client: can't get socket\n");
00383 ast_config_destroy(cfg);
00384 return -1;
00385 }
00386
00387 memset(&serv_addr, 0, sizeof(serv_addr));
00388
00389 if ((serv_addr.sin_addr.s_addr = inet_addr(host)) == -1) {
00390
00391 serverhost = ast_gethostbyname(host, &ahp);
00392
00393 if (serverhost == NULL) {
00394 ast_log(LOG_WARNING, "festival_client: gethostbyname failed\n");
00395 ast_config_destroy(cfg);
00396 return -1;
00397 }
00398 memmove(&serv_addr.sin_addr, serverhost->h_addr, serverhost->h_length);
00399 }
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 return -1;
00408 }
00409
00410
00411 MD5Init(&md5ctx);
00412 MD5Update(&md5ctx, (unsigned char *)args.text, strlen(args.text));
00413 MD5Final(MD5Res, &md5ctx);
00414 MD5Hex[0] = '\0';
00415
00416
00417
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, AST_FILE_MODE);
00429 if (fdesc != -1) {
00430 writecache = 1;
00431 strln = strlen(args.text);
00432 ast_debug(1, "line length : %d\n", strln);
00433 if (write(fdesc,&strln,sizeof(int)) < 0) {
00434 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
00435 }
00436 if (write(fdesc,data,strln) < 0) {
00437 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
00438 }
00439 seekpos = lseek(fdesc, 0, SEEK_CUR);
00440 ast_debug(1, "Seek position : %d\n", seekpos);
00441 }
00442 } else {
00443 if (read(fdesc,&strln,sizeof(int)) != sizeof(int)) {
00444 ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
00445 }
00446 ast_debug(1, "Cache file exists, strln=%d, strlen=%d\n", strln, (int)strlen(args.text));
00447 if (strlen(args.text) == strln) {
00448 ast_debug(1, "Size OK\n");
00449 if (read(fdesc,&bigstring,strln) != strln) {
00450 ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
00451 }
00452 bigstring[strln] = 0;
00453 if (strcmp(bigstring, args.text) == 0) {
00454 readcache = 1;
00455 } else {
00456 ast_log(LOG_WARNING, "Strings do not match\n");
00457 }
00458 } else {
00459 ast_log(LOG_WARNING, "Size mismatch\n");
00460 }
00461 }
00462 }
00463
00464 if (readcache == 1) {
00465 close(fd);
00466 fd = fdesc;
00467 ast_debug(1, "Reading from cache...\n");
00468 } else {
00469 ast_debug(1, "Passing text to festival...\n");
00470 fs = fdopen(dup(fd), "wb");
00471
00472 fprintf(fs, "%s", festivalcommand);
00473 fflush(fs);
00474 fclose(fs);
00475 }
00476
00477
00478 if (writecache == 1) {
00479 ast_debug(1, "Writing result to cache...\n");
00480 while ((strln = read(fd, buffer, 16384)) != 0) {
00481 if (write(fdesc,buffer,strln) < 0) {
00482 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
00483 }
00484 }
00485 close(fd);
00486 close(fdesc);
00487 fd = open(cachefile, O_RDWR);
00488 lseek(fd, seekpos, SEEK_SET);
00489 }
00490
00491 ast_debug(1, "Passing data to channel...\n");
00492
00493
00494
00495 wave = 0;
00496 do {
00497 int read_data;
00498 for (n = 0; n < 3; ) {
00499 read_data = read(fd, ack + n, 3 - n);
00500
00501
00502
00503 if (read_data == -1) {
00504 ast_log(LOG_WARNING, "Unable to read from cache/festival fd\n");
00505 close(fd);
00506 ast_config_destroy(cfg);
00507 return -1;
00508 }
00509 n += read_data;
00510 }
00511 ack[3] = '\0';
00512 if (strcmp(ack, "WV\n") == 0) {
00513 ast_debug(1, "Festival WV command\n");
00514 if ((waveform = socket_receive_file_to_buff(fd, &filesize))) {
00515 res = send_waveform_to_channel(chan, waveform, filesize, args.interrupt);
00516 ast_free(waveform);
00517 }
00518 break;
00519 } else if (strcmp(ack, "LP\n") == 0) {
00520 ast_debug(1, "Festival LP command\n");
00521 if ((waveform = socket_receive_file_to_buff(fd, &filesize))) {
00522 waveform[filesize] = '\0';
00523 ast_log(LOG_WARNING, "Festival returned LP : %s\n", waveform);
00524 ast_free(waveform);
00525 }
00526 } else if (strcmp(ack, "ER\n") == 0) {
00527 ast_log(LOG_WARNING, "Festival returned ER\n");
00528 res = -1;
00529 break;
00530 }
00531 } while (strcmp(ack, "OK\n") != 0);
00532 close(fd);
00533 ast_config_destroy(cfg);
00534 return res;
00535 }
00536
00537 static int unload_module(void)
00538 {
00539 return ast_unregister_application(app);
00540 }
00541
00542 static int load_module(void)
00543 {
00544 struct ast_flags config_flags = { 0 };
00545 struct ast_config *cfg = ast_config_load(FESTIVAL_CONFIG, config_flags);
00546 if (!cfg) {
00547 ast_log(LOG_WARNING, "No such configuration file %s\n", FESTIVAL_CONFIG);
00548 return AST_MODULE_LOAD_DECLINE;
00549 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
00550 ast_log(LOG_ERROR, "Config file " FESTIVAL_CONFIG " is in an invalid format. Aborting.\n");
00551 return AST_MODULE_LOAD_DECLINE;
00552 }
00553 ast_config_destroy(cfg);
00554 return ast_register_application_xml(app, festival_exec);
00555 }
00556
00557 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Simple Festival Interface");