Connect to festival. More...
#include "asterisk.h"#include <sys/socket.h>#include <netdb.h>#include <netinet/in.h>#include <arpa/inet.h>#include <signal.h>#include <fcntl.h>#include <ctype.h>#include <errno.h>#include "asterisk/file.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/app.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 , .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 = "0901e4e500243c855563a2d78b0c03e4" , .load = load_module, .unload = unload_module, } |
| static char * | app = "Festival" |
| static struct ast_module_info * | ast_module_info = &__mod_info |
Connect to festival.
Definition in file app_festival.c.
| #define FESTIVAL_CONFIG "festival.conf" |
Definition at line 53 of file app_festival.c.
Referenced by festival_exec(), and load_module().
| #define MAXFESTLEN 2048 |
Definition at line 55 of file app_festival.c.
Referenced by festival_exec().
| #define MAXLEN 180 |
Definition at line 54 of file app_festival.c.
| static void __reg_module | ( | void | ) | [static] |
Definition at line 557 of file app_festival.c.
| static void __unreg_module | ( | void | ) | [static] |
Definition at line 557 of file app_festival.c.
| static int festival_exec | ( | struct ast_channel * | chan, | |
| void * | vdata | |||
| ) | [static] |
Definition at line 268 of file app_festival.c.
References AST_APP_ARG, ast_config_destroy(), ast_config_load, ast_debug, AST_DECLARE_APP_ARGS, AST_DIGIT_ANY, ast_free, ast_gethostbyname(), ast_log(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), ast_true(), ast_variable_retrieve(), CONFIG_STATUS_FILEINVALID, errno, FESTIVAL_CONFIG, LOG_ERROR, LOG_WARNING, MAXFESTLEN, MD5Final(), MD5Init(), MD5Update(), send_waveform_to_channel(), socket_receive_file_to_buff(), text, and wave.
Referenced by load_module().
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 { /* This else parses the festivalcommand that we're sent from the config file for \n's, etc */ 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); /* we know it is big enough */ 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 /* Connect to local festival server */ 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 /* its a name rather than an ipnum */ 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 /* Compute MD5 sum of string */ 00411 MD5Init(&md5ctx); 00412 MD5Update(&md5ctx, (unsigned char *)args.text, strlen(args.text)); 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, 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 /* Write to cache and then pass it down */ 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 /* Read back info from server */ 00494 /* This assumes only one waveform will come back, also LP is unlikely */ 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 /* this avoids falling in infinite loop 00501 * in case that festival server goes down 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) { /* receive a waveform */ 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) { /* receive an s-expr */ 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) { /* server got an error */ 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 }
| 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_xml, CONFIG_STATUS_FILEINVALID, FESTIVAL_CONFIG, festival_exec(), LOG_ERROR, and LOG_WARNING.
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 }
| static int send_waveform_to_channel | ( | struct ast_channel * | chan, | |
| char * | waveform, | |||
| int | length, | |||
| char * | intkeys | |||
| ) | [static] |
Definition at line 161 of file app_festival.c.
References ast_channel::_state, ast_answer(), ast_debug, 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_frame::frametype, LOG_WARNING, ast_frame::offset, ast_frame::samples, send_waveform_to_fd(), ast_frame::subclass, and ast_channel::writeformat.
Referenced by festival_exec().
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 /* Answer if it's not already going */ 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 /* Order is important -- there's almost always going to be mp3... we want to prioritize the 00199 user */ 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 /* Treat as a generator */ 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) { /* last frame */ 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 }
| static int send_waveform_to_fd | ( | char * | waveform, | |
| int | length, | |||
| int | fd | |||
| ) | [static] |
Definition at line 127 of file app_festival.c.
References ast_close_fds_above_n(), ast_log(), ast_opt_high_priority, ast_safe_fork(), ast_set_priority(), errno, and LOG_WARNING.
Referenced by send_waveform_to_channel().
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 }
| static char* socket_receive_file_to_buff | ( | int | fd, | |
| int * | size | |||
| ) | [static] |
Definition at line 76 of file app_festival.c.
References ast_free, ast_malloc, ast_realloc, and buff.
Referenced by festival_exec().
00077 { 00078 /* Receive file (probably a waveform file) from socket using 00079 * Festival key stuff technique, but long winded I know, sorry 00080 * but will receive any file without closing the stream or 00081 * using OOB data 00082 */ 00083 static char *file_stuff_key = "ft_StUfF_key"; /* must == Festival's 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; /* hit stream eof before end of file */ 00098 if ((*size) + k + 1 >= bufflen) { 00099 /* +1 so you can add a terminating NULL if you want */ 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 /* It looked like the key but wasn't */ 00111 for (i = 0; i < k; i++, (*size)++) 00112 buff[*size] = file_stuff_key[i]; 00113 k = 0; 00114 /* omit the stuffed 'X' */ 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 }
| static int unload_module | ( | void | ) | [static] |
Definition at line 537 of file app_festival.c.
References ast_unregister_application().
00538 { 00539 return ast_unregister_application(app); 00540 }
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .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 = "0901e4e500243c855563a2d78b0c03e4" , .load = load_module, .unload = unload_module, } [static] |
Definition at line 557 of file app_festival.c.
char* app = "Festival" [static] |
Definition at line 74 of file app_festival.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 557 of file app_festival.c.
1.6.2