1 /*
2 * YASR ("Yet Another Screen Reader") is an attempt at a lightweight,
3 * portable screen reader.
4 *
5 * Copyright (C) 2001-2002 by Michael P. Gorse. All rights reserved.
6 *
7 * YASR comes with ABSOLUTELY NO WARRANTY.
8 *
9 * This is free software, placed under the terms of the
10 * GNU Lesser General Public License, as published by the Free Software
11 * Foundation. Please see the file COPYING for details.
12 *
13 * Web Page: http://yasr.sf.net
14 *
15 * This software is maintained by:
16 * Michael P. Gorse <mgorse@users.sourceforge.net>
17 */
18
19 #include "yasr.h"
20 #include <sys/socket.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
23
24 #ifndef TRUE
25 #define TRUE 1
26 #endif
27 #ifndef FALSE
28 #define FALSE 1
29 #endif
30
31 char ttsbuf[80];
32
33 static Tts_synth synth[] = {
34 {"", "%s\r", "\030", "\005c3", "\005c0", TRUE, "", ""}, /* speakout */
35 {"", "%s[:syn]", "\003", "[:sa le]", "[:sa c]", FALSE, "[]{}\\|_@#^*<>\"`~", ""}, /* DECtalk */
36 {"s\r", "q {%s}\rd\r", "s\r", "l {%c}\r", NULL, FALSE, "[]{}\\|_@#^*<>\"`~^", "exit"}, /* emacspeak server */
37 {"", "%s", "\030", "\001c", "\001t", TRUE, "", ""}, /* doubletalk */
38 {"", "%s\r\r", "\030\030\r\r", NULL, NULL, TRUE, "", ""}, /* BNS */
39 {"", "%s\r\n", "\030", "@P1", "@P0", TRUE, "@", ""}, /* Apollo */
40 {"(audio_mode 'async)\n",
41 "(SayText \"%s\")\n",
42 "(audio_mode 'shutup)\n",
43 "(SayText '%c)\n",
44 NULL,
45 TRUE,
46 "",
47 "\003\")\n"}, /* festival */
48 {"", "%s\r\n", "\033", "%c\r", NULL, TRUE, "@", ""}, /* Ciber232 */
49 /* speech dispatcher (note: various things handled with custom code) */
50 {NULL, NULL, "CANCEL SELF\r\n", NULL, NULL, FALSE, "", "quit\r\n"},
51 };
52
53 static char *dict[256];
54 static int tts_flushed = 0;
55
56
dict_read(char * buf)57 int dict_read(char *buf)
58 {
59 int c;
60 char *p;
61
62 if (isdigit((int) buf[0]))
63 {
64 c = strtol(buf, &p, 0);
65 if (c > 255)
66 {
67 return (1);
68 }
69 p++;
70 } else
71 {
72 c = buf[0];
73 p = buf + 2;
74 }
75 dict[c] = strdup(p);
76
77 return (0);
78 }
79
80
dict_write(FILE * fp)81 void dict_write(FILE * fp)
82 {
83 int i;
84
85 for (i = 0; i < 256; i++)
86 {
87 if (dict[i])
88 {
89 if (i > 31 && i < 127 && i != 35 && i != 91)
90 {
91 (void) fprintf(fp, "%c=%s\n", i, dict[i]);
92 } else
93 {
94 (void) fprintf(fp, "0x%.2x=%s\n", i, dict[i]);
95 }
96 }
97 }
98 }
99
100
101 /* Search for a program on the path */
102
tts_flush()103 void tts_flush()
104 {
105 if (tts.outlen)
106 {
107 speak(tts.buf, tts.outlen);
108 }
109
110 tts.outlen = 0;
111
112 }
113
114 #if 0
115 static void tts_wait(int usecs)
116 {
117 char buf[100];
118
119 if (usecs != -1 && !readable(tts.fd, usecs)) return;
120 while (readable(tts.fd, 0))
121 {
122 read(tts.fd, buf, sizeof(buf));
123 }
124 }
125 #endif
126
tts_silence()127 void tts_silence()
128 {
129 char tmp[1] = { 0 };
130
131 if (tts_flushed)
132 {
133 return;
134 }
135 (void) tcflush(tts.fd, TCOFLUSH);
136 tts.obufhead = tts.obuftail = tts.flood = 0;
137 opt_queue_empty(1);
138 tts_send(synth[tts.synth].flush, strlen(synth[tts.synth].flush));
139 tts.oflag = 0;
140 tts_flushed = 1;
141
142 if (tts.synth == TTS_DECTALK)
143 {
144 do
145 {
146 if (!readable(tts.fd, 50000)) break;
147 if (read(tts.fd, tmp, 1) == -1)
148 {
149 perror("tts_silence");
150 break;
151 }
152 } while (tmp[0] != 1 && tmp[0] != 0);
153 /* Following is a hack; without it, [:sa c] could have been sent and
154 lost by the DEC-talk if a silence happens immediately after it */
155 tts_send("[:sa c]", 7);
156 tts.oflag = 0; /* pretend we didn't just do that */
157 }
158 }
159
160
tts_obufpack()161 static void tts_obufpack()
162 {
163 (void) memmove(tts.obuf, tts.obuf + tts.obufhead,
164 tts.obuftail - tts.obufhead);
165 tts.obuftail -= tts.obufhead;
166 tts.obufhead = 0;
167 }
168
169
170 #ifdef TTSLOG
171 static int ofd;
172 #endif
173
174
tts_obufout(int x)175 /*ARGSUSED*/ static void tts_obufout(int x)
176 {
177 int len, len2;
178 int oldoflag;
179
180 if (!tts.flood)
181 {
182 opt_queue_empty(2);
183 }
184 oldoflag = tts.oflag;
185 while (tts.obufhead < tts.obuftail)
186 {
187 len = strlen(tts.obuf + tts.obufhead);
188 if (len > 1024)
189 {
190 len = 1024;
191 }
192 len2 = write(tts.fd, tts.obuf + tts.obufhead, len);
193 #ifdef TTSLOG
194 (void) write(ofd, tts.obuf + tts.obufhead, len2);
195 #endif
196 tts.obufhead += len2;
197 if (len2 < len)
198 {
199 tts.oflag = oldoflag;
200 (void) signal(SIGALRM, &tts_obufout);
201 alarm(1);
202 return;
203 }
204 while (tts.obufhead < tts.obuftail && !tts.obuf[tts.obufhead])
205 {
206 tts.obufhead++;
207 }
208 }
209 tts.flood = 0;
210 tts.oflag = oldoflag;
211 (void) signal(SIGALRM, &tts_obufout);
212 }
213
214
tts_addbuf(char * buf,int len,int len2)215 static void tts_addbuf(char *buf, int len, int len2)
216 {
217 char *ptr;
218
219 tts.flood = 1;
220 if (len2 == -1)
221 {
222 ptr = buf;
223 } else
224 {
225 ptr = buf + len2;
226 len -= len2;
227 }
228 if (tts.obuftail + len > tts.obuflen)
229 {
230 if ((tts.obuftail - tts.obufhead) + len > tts.obuflen)
231 {
232 tts.obuflen += 1024 + len - (len % 1024);
233 tts.obuf = realloc(tts.obuf, tts.obuflen);
234 } else
235 {
236 tts_obufpack();
237 }
238 }
239 (void) memcpy(tts.obuf + tts.obuftail, ptr, len);
240 tts.obuftail += len;
241 tts.obuf[tts.obuftail++] = 0;
242 (void) alarm(1);
243 }
244
245
tts_send(char * buf,int len)246 void tts_send(char *buf, int len)
247 {
248 int len2;
249
250 if (!len)
251 {
252 return;
253 }
254 if (tts_flushed)
255 {
256 tts_flushed = 0;
257 }
258 #ifndef SILENT
259 if (!tts.flood)
260 {
261 len2 = write(tts.fd, buf, len);
262
263 #ifdef TTSLOG
264 (void) write(ofd, buf, len2);
265 #endif
266
267 if (len2 < len)
268 {
269 tts_addbuf(buf, len, len2);
270 }
271 } else
272 tts_addbuf(buf, len, 0);
273 #endif
274
275 tts.oflag = 1;
276 }
277
278
unspeakable(unsigned char ch)279 static int unspeakable(unsigned char ch)
280 {
281 char *p = synth[tts.synth].unspeakable;
282
283 if (ch < 32) return 1;
284 while (*p)
285 {
286 if (*p++ == ch) return 1;
287 }
288 return (0);
289 }
290
tts_end()291 void tts_end()
292 {
293 tts_send(synth[tts.synth].end, strlen(synth[tts.synth].end));
294 }
295
tts_out(unsigned char * buf,int len)296 void tts_out(unsigned char *buf, int len)
297 {
298 char obuf[1024];
299 char *p;
300 int obo = 0; /* current offset into obuf */
301 int i;
302 int xml = 0;
303
304 if (!len) return;
305 opt_queue_empty(0);
306 if (tts.synth == TTS_SPEECHD)
307 {
308 char *q;
309 tts_send("SPEAK\r\n", 7);
310 if (buf[0] == '.' && buf[1] == '\r') tts_send(".", 1);
311 for (p = (char *)buf; (q = strstr(p + 1, "\r.\r")) && q < (char *)buf+len; p = q)
312 {
313 tts_send(p, q - p);
314 }
315 tts_send(p, (long)buf + len - (long)p);
316 tts_send("\r\n.\r\n", 5);
317 return;
318 }
319 p = synth[tts.synth].say;
320 opt_queue_empty(0);
321 while (*p)
322 {
323 if (*p == '%')
324 {
325 p++;
326 for (i = 0; i < len; i++)
327 {
328 if (unspeakable((unsigned char)buf[i]))
329 {
330 if (obo > 0 && obuf[obo - 1] != ' ')
331 {
332 obuf[obo++] = ' ';
333 tts_send(obuf, obo);
334 obo = 0;
335 }
336 if (dict[buf[i]])
337 {
338 (void) strcpy(obuf + obo, dict[buf[i]]);
339 obo = strlen(obuf);
340 }
341 obuf[obo++] = 32;
342 }
343 else if (xml)
344 {
345 switch (buf[i])
346 {
347 /* For some reason, < does not work for me with Voxeo */
348 case '<': strcpy(obuf + obo, " less than "); obo += 11; break;
349 case '>': strcpy(obuf + obo, ">"); obo += 4; break;
350 case '&': strcpy(obuf + obo, "&"); obo += 5; break;
351 default: obuf[obo++] = buf[i]; break;
352 }
353 }
354 else
355 {
356 if (ui.split_caps && i > 0 && islower(buf[i-1]) && isupper(buf[i]))
357 {
358 obuf[obo++] = ' ';
359 }
360 obuf[obo++] = buf[i];
361 }
362 if (obo > (sizeof(obuf) / sizeof(obuf[0])) - 6)
363 {
364 tts_send(obuf, obo);
365 obo = 0;
366 }
367 }
368 }
369 else
370 {
371 obuf[obo++] = *p;
372 }
373 p++;
374 }
375 tts_send(obuf, obo);
376 }
377
tts_say(char * buf)378 void tts_say(char *buf)
379 {
380 tts_out((unsigned char *) buf, strlen(buf));
381 }
382
383
tts_say_printf(char * fmt,...)384 void tts_say_printf(char *fmt, ...)
385 {
386 va_list arg;
387 char buf[200];
388
389 va_start(arg, fmt);
390 (void) vsnprintf(buf, sizeof(buf), fmt, arg);
391 tts_say(buf);
392 va_end(arg);
393 }
394
395
tts_saychar(unsigned char ch)396 void tts_saychar(unsigned char ch)
397 {
398 int i, j = 0;
399 int stack[10];
400
401 if (!ch)
402 {
403 ch = 32;
404 }
405 if (tts.synth == TTS_SPEECHD)
406 {
407 if (ch == 32) tts_send("CHAR space\r\n", 12);
408 else tts_printf_ll("CHAR %c\r\n", ch);
409 return;
410 }
411 if (unspeakable(ch) && dict[ch])
412 {
413 tts_say(dict[ch]);
414 return;
415 }
416 if (!synth[tts.synth].charoff)
417 { /* assume on string does everything */
418 (void) sprintf(ttsbuf, synth[tts.synth].charon, ch);
419 tts_send(ttsbuf, strlen(ttsbuf));
420 return;
421 }
422
423 for (i = 0; i < NUMOPTS; i++)
424 {
425 if ((opt[i].type & 0x3f) != OT_TREE && !opt[i].ptr && opt[i].synth == tts.synth)
426 {
427 stack[j++] = i;
428 stack[j++] = opt_getval(i, 0);
429 opt_set(i, &opt[i].v.enum_max);
430 }
431 }
432 ttsbuf[1] = 0;
433 opt_queue_empty(3);
434 ttsbuf[0] = ch;
435 tts_send(ttsbuf, 1);
436 if (synth[tts.synth].saychar_needs_flush)
437 {
438 tts_send(synth[tts.synth].say + 2, strlen(synth[tts.synth].say) - 2);
439 }
440 while (j)
441 {
442 j -= 2;
443 opt_set(stack[j], stack + j + 1);
444 }
445 }
446
447
448 static char *ph_alph[] = {
449 "alpha", "bravo", "charlie", "delta", "echo",
450 "foxtrot", "golf", "hotel", "india", "juliet",
451 "kilo", "lima", "mike", "november", "oscar",
452 "papa", "quebec", "romeo", "sierra", "tango",
453 "uniform", "victor", "whisky", "x ray",
454 "yankee", "zulu"
455 };
456
457
tts_sayphonetic(unsigned char ch)458 void tts_sayphonetic(unsigned char ch)
459 {
460 if (!isalpha(ch))
461 {
462 tts_saychar(ch);
463 return;
464 }
465 if (isupper(ch))
466 {
467 tts_say(_("cap"));
468 } else
469 {
470 ch = toupper(ch);
471 }
472 tts_say(ph_alph[--ch & 0x3f]);
473 }
474
open_tcp(char * port)475 static int open_tcp(char *port)
476 {
477 char host[1024];
478 int portnum;
479 int fd;
480 struct sockaddr_in servaddr;
481 char *p;
482 int len;
483
484 p = strstr(port, ":");
485 portnum = atoi(p + 1);
486 len = p - port;
487 if (len > 1023)
488 {
489 fprintf(stderr, "synthesizer port too long\n");
490 exit(1);
491 }
492 memcpy(host, port, len);
493 host[len] = '\0';
494 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
495 {
496 perror("socket");
497 exit(1);
498 }
499 memset(&servaddr, 0, sizeof(servaddr));
500 servaddr.sin_family = AF_INET;
501 servaddr.sin_port = htons(portnum);
502
503 #ifdef HAVE_INET_PTON
504 if (inet_pton(AF_INET, host, &servaddr.sin_addr) <= 0)
505 {
506 perror("inet_pton");
507 exit(1);
508 }
509 #else
510 servaddr.sin_addr.s_addr = inet_addr(host);
511 #endif
512
513 if (connect(fd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
514 {
515 perror("connect");
516 exit(1);
517 }
518 return fd;
519 }
520
tts_init(int first_call)521 int tts_init( int first_call)
522 {
523 struct termios t;
524 char *arg[8];
525 int i;
526 char *portname;
527 int mode = O_RDWR | O_NONBLOCK;
528 char buf[100];
529
530 portname = getfn(tts.port);
531 tts.pid = 0;
532 tts.reinit = !first_call;
533
534 (void) signal(SIGALRM, &tts_obufout);
535
536 #ifdef TTSLOG
537 ofd = open("tts.log", O_WRONLY | O_CREAT);
538 if (ofd == -1)
539 {
540 perror("open");
541 }
542 #endif
543 if (first_call && tts.port[0] != '|' && strstr(tts.port, ":"))
544 {
545 tts.fd = open_tcp(tts.port);
546 }
547 else if (tts.port[0] != '|')
548 {
549 if (tts.synth == TTS_DECTALK)
550 {
551 mode = O_NOCTTY | O_RDWR;
552 }
553 else if (tts.synth == TTS_EMACSPEAK_SERVER)
554 {
555 mode = O_WRONLY;
556 }
557 if (first_call)
558 {
559 tts.fd = open(portname, mode);
560 }
561 if (tts.fd == -1)
562 {
563 perror("tts");
564 exit(1);
565 }
566 (void) tcgetattr(tts.fd, &t);
567 if (tts.synth == TTS_DECTALK)
568 {
569 /* When sending a ^C, make sure we send a ^C and not a break */
570 cfmakeraw(&t);
571 /* following two lines match the Emacspeak server */
572 t.c_iflag |= IXON|IXOFF;
573 t.c_lflag |= IEXTEN;
574 /* When the DEC-talk powers on, it sends several ^A's. Discard them */
575 tcflush(tts.fd, TCIFLUSH);
576 }
577 t.c_cflag |= CRTSCTS | CLOCAL;
578 t.c_cc[VMIN] = 0;
579 t.c_cc[VTIME] = 10;
580 cfsetospeed(&t, B9600);
581 (void) tcsetattr(tts.fd, 0, &t);
582 }
583 else
584 { /* a pipe */
585 (void) strcpy(buf, tts.port + 1);
586 arg[i = 0] = strtok(buf, " ");
587 while (i < 7)
588 {
589 if (!(arg[++i] = strtok(NULL, " ")))
590 {
591 break;
592 }
593 }
594
595 if (first_call)
596 {
597 if (openpty(&tts.fd, &tts.fd_slave, NULL, NULL, NULL) == -1)
598 {
599 perror("openpty");
600 exit(1);
601 }
602 }
603
604 if (!(tts.pid = fork()))
605 {
606 /* child -- set up tty */
607 (void) dup2(tts.fd_slave, 0);
608 (void) dup2(tts.fd_slave, 1);
609 (void) dup2(tts.fd_slave, 2);
610 /* tts.fd_slave is not closed */
611 }
612 if (!tts.pid)
613 {
614 (void) execvp(arg[0], arg);
615 perror("execpv");
616 }
617 (void) usleep(10000);
618 if (tts.pid == -1)
619 {
620 perror("forkpty");
621 }
622 }
623 if (tts.synth == TTS_SPEECHD)
624 {
625 char buf[200];
626 char *logname = getenv("LOGNAME");
627 if (logname == NULL) logname = getlogin();
628 tts_printf_ll("SET self CLIENT_NAME %s:yasr:tts\r\n", logname);
629 }
630 else tts_send(synth[tts.synth].init, strlen(synth[tts.synth].init));
631
632 /* init is now finished */
633 tts.reinit = 0;
634
635 return (0);
636 }
637
tts_reinit2()638 int tts_reinit2()
639 {
640 tts_init(0);
641 tts_initsynth(NULL);
642 return (0);
643 }
644
tts_addchr(char ch)645 void tts_addchr(char ch)
646 {
647 tts.buf[tts.outlen++] = ch;
648 if (tts.outlen > 250)
649 {
650 tts_flush();
651 }
652 }
653
654
tts_initsynth(int * argp)655 /*ARGSUSED*/ void tts_initsynth(int *argp)
656 {
657 int i, v;
658
659 for (i = 0; i < NUMOPTS; i++)
660 {
661 if ((opt[i].type & 0x40) &&
662 opt[i].synth == tts.synth && (opt[i].type & 0x3f) != 0x03)
663 {
664 v = opt_getval(i, 0);
665 opt_set(i, &v);
666 }
667 }
668 if (!ui.silent)
669 {
670 tts_say(_("Synthesizer reinitialized."));
671 }
672 }
673
674
675
tts_reinit(int * argp)676 /*ARGSUSED*/ void tts_reinit(int *argp)
677 {
678 int pid=tts.pid;
679
680 if (pid==0)
681 {
682 return;
683 }
684
685 tts.reinit=1; /* Start reinit */
686
687 tts_silence();
688 usleep(500000);
689
690 if (kill (pid, SIGTERM)!=0)
691 {
692 if (errno==ESRCH)
693 {
694 tts_reinit2();
695 }
696 else
697 {
698 kill (pid, SIGKILL);
699 }
700 }
701
702 /* wait init completion (tts.fd must be available) */
703 while(tts.reinit)
704 {
705 usleep(100000);
706 }
707 }
708
709
tts_charon()710 void tts_charon()
711 {
712 ttssend(synth[tts.synth].charon);
713 }
714
715
tts_charoff()716 void tts_charoff()
717 {
718 ttssend(synth[tts.synth].charoff);
719 }
720
721
tts_printf_ll(const char * str,...)722 void tts_printf_ll(const char *str, ...)
723 {
724 char buf[200];
725 va_list args;
726
727 va_start(args, str);
728 vsprintf(buf, str, args);
729 tts_send(buf, strlen(buf));
730 }
731