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