From 0cc9b20c15460213e488bf5e70963b941482f628 Mon Sep 17 00:00:00 2001 From: William Harrington Date: Tue, 14 Jan 2025 16:06:02 -0600 Subject: Add source. --- spo256/Makefile.in | 34 ++++++ spo256/README | 24 ++++ spo256/chars.c | 339 ++++++++++++++++++++++++++++++++++++++++++++++++++++ spo256/client.c | 40 +++++++ spo256/currency.c | 43 +++++++ spo256/getidx.c | 98 +++++++++++++++ spo256/getspo.c | 68 +++++++++++ spo256/number.c | 132 ++++++++++++++++++++ spo256/speak.1 | 46 +++++++ spo256/speak.8 | 39 ++++++ spo256/speak.c | 86 +++++++++++++ spo256/speak.conf.7 | 72 +++++++++++ spo256/speak.h | 48 ++++++++ spo256/speak.init | 54 +++++++++ spo256/spo.c | 161 +++++++++++++++++++++++++ spo256/spo256.8 | 98 +++++++++++++++ spo256/word.c | 38 ++++++ spo256/words.c | 34 ++++++ 18 files changed, 1454 insertions(+) create mode 100644 spo256/Makefile.in create mode 100644 spo256/README create mode 100644 spo256/chars.c create mode 100644 spo256/client.c create mode 100644 spo256/currency.c create mode 100644 spo256/getidx.c create mode 100644 spo256/getspo.c create mode 100644 spo256/number.c create mode 100644 spo256/speak.1 create mode 100644 spo256/speak.8 create mode 100644 spo256/speak.c create mode 100644 spo256/speak.conf.7 create mode 100644 spo256/speak.h create mode 100644 spo256/speak.init create mode 100644 spo256/spo.c create mode 100644 spo256/spo256.8 create mode 100644 spo256/word.c create mode 100644 spo256/words.c (limited to 'spo256') diff --git a/spo256/Makefile.in b/spo256/Makefile.in new file mode 100644 index 0000000..6e21ea4 --- /dev/null +++ b/spo256/Makefile.in @@ -0,0 +1,34 @@ +# +# Template to build spo256-AL2 text-to-speech server. +# $Id: Makefile.in 1.2 Mon, 24 Mar 1997 12:25:37 -0500 dyfet $ +# Copyright (c) 1997 by Tycho Softworks. +# + +OBJS = speak.o spo.o getspo.o words.o chars.o getidx.o client.o \ + number.o currency.o +PROGS = spo256 +START = 10 +RUNLVLS = 3 + +all: $(PROGS) + +install: $(PROGS) + install -s -g bin $(PROGS) $(SBINDIR) + ln -sf $(SBINDIR)/spo256 $(BINDIR)/speak + install -g bin speak.conf $(SYSCONFDIR) + ../sdk/bin/instinit speak $(START) $(RUNLVLS) + ../sdk/bin/instsvc services speak 800/tcp \# SPO256-AL2 Text-to-Speech service + ../sdk/bin/instman $(MANDIR) 1 speak + ../sdk/bin/instman $(MANDIR) 7 speak.conf + ../sdk/bin/instman $(MANDIR) 8 spo256 + +clean: + -rm $(PROGS) + -rm $(OBJS) + +.c.o: + $(CC) $(CFLAGS) $(OPTIMIZE) -I../sdk -o $@ -c $< + +spo256: $(OBJS) + $(CC) $(CFLAGS) -L../sdk/lib $(LIBS) -o $@ $(OBJS) $(LIBS) + diff --git a/spo256/README b/spo256/README new file mode 100644 index 0000000..29c2fae --- /dev/null +++ b/spo256/README @@ -0,0 +1,24 @@ +This particular SPO256 server is able to translate common usages for numbers +and perform word substitution. For example, the number 123.45 is pronounced +as "one hundred twenty three point four five". Internet names and addresses +are also pronounced correctly to a large extent. The support of different +usages and idioms in this server is not yet as complete as the WorldVU SPO +server, but this is being worked on. + +The SPO server was rewritten in release 0.3 with a character state parser +to allow better parsing for and pacing of spoken text. The 0.3 state +parser now recognizes embedded server command sequences as well as text +that is to be spoken. Embedded server commands are proceeded by the + character, and appear in <>'s, using a format much like mark-up tags. +For example, word "spelling" can be turned on or off with and +. The current tags supported by the SPO256 server are found +in the spo256 man page. More tags will be added as they appear feasable to +create. + +This SPO server differs from the original WorldVU server in that it can +also be used in a client role to pipe standard output to the server. One +can enter "cat filename | spo256 localhost" (or "cat filename | speak +localhost") to redirect output to the spo server running on the local +machine, for example. + + diff --git a/spo256/chars.c b/spo256/chars.c new file mode 100644 index 0000000..e4b2a2a --- /dev/null +++ b/spo256/chars.c @@ -0,0 +1,339 @@ +/* + * Character state parser for text-to-speech server. + * $Id: chars.c 1.2 Mon, 24 Mar 1997 12:25:37 -0500 dyfet $ + * Copyright (c) 1997 by Tycho Softworks. + * For conditions of distribution and reuse see product license. + */ + +#include +#include +#include +#include +#include "speak.h" + +enum +{ + CH_IGNORE, + CH_PERIOD, + CH_QUOTE, + CH_ESCAPE, + CH_SPACE, + CH_ALPHA, + CH_NUMERIC, + CH_PUNCT, +}; + +static uchar map[256]; +static uchar word[256]; +static int cpos = 0; +static bool init = FALSE; +static void (*state)(uchar ch); +static bool quote; + +static void s_hex(uchar ch); +static void s_num(uchar ch); +static void s_cmd(uchar ch); +static void s_leadcmd(uchar ch); +static void s_abbrev(uchar ch); +static void s_point(uchar ch); +static void s_word(uchar ch); +static void s_idle(uchar ch); + +static void s_hex(uchar ch) +{ + switch(ch) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'a': + case 'A': + case 'b': + case 'B': + case 'c': + case 'C': + case 'd': + case 'D': + case 'e': + case 'E': + case 'f': + case 'F': + word[cpos++] = ch; + break; + default: + word[cpos] = 0; + number(word); + state = s_idle; + s_idle(ch); + } +} + +static void s_num(uchar ch) +{ + switch(map[ch]) + { + case CH_NUMERIC: + word[cpos++] = ch; + break; + default: + switch(ch) + { + case 'e': + case '^': + word[cpos++] = ch; + number(word); + cpos = 0; + spo_word("raised"); + spo_word("to"); + spo_word("the"); + spo_word("power"); + break; + case ':': + case '/': + case '.': + word[cpos++] = ch; + break; + case '%': + word[cpos] = 0; + number(word); + state = s_idle; + spo_word("percent"); + break; + case 'x': + case 'X': + if(cpos == 1) + { + cpos = 0; + state = s_hex; + break; + } + default: + word[cpos] = 0; + number(word); + state = s_idle; + s_idle(ch); + } + } +} + +static void s_cmd(uchar ch) +{ + if(ch == '>') + { + word[cpos] = 0; + spo_cmd((char *)word); + state = s_idle; + } + else + word[cpos++] = ch; +} + +static void s_leadcmd(uchar ch) +{ + if(ch == '<') + state = s_cmd; + else + { + state = s_idle; + s_idle(ch); + } +} + +static void s_abbrev(uchar ch) +{ + switch(map[ch]) + { + case CH_SPACE: + abbrev((char *)word); + state = s_idle; + break; + default: + spo_word((char *)word); + cpos = 0; + spo_word("dot"); + state = s_word; + s_word(ch); + } +} + +static void s_point(uchar ch) +{ + switch(map[ch]) + { + case CH_ALPHA: + state = s_word; + case CH_PERIOD: + spo_word("dot"); + break; + case CH_NUMERIC: + spo_word("point"); + state = s_num; + break; + default: + state = s_idle; + break; + } +} + +static void s_word(uchar ch) +{ + switch(map[ch]) + { + case CH_PERIOD: + if(!cpos) + state = s_point; + else + state = s_abbrev; + word[cpos++] = '.'; + word[cpos] = 0; + break; + case CH_PUNCT: + case CH_ALPHA: + case CH_NUMERIC: + if(cpos < 255) + word[cpos++] = ch; + break; + case CH_SPACE: + word[cpos] = 0; + spo_word((char *)word); + state = s_idle; + break; + default: + switch(ch) + { + case '\'': + word[cpos++] = ch; + break; + default: + word[cpos] = 0; + spo_word((char *)word); + spo_pause(P_WORD); + state = s_idle; + s_idle(ch); + } + } +} + +static void s_idle(uchar ch) +{ + cpos = 0; + switch(map[ch]) + { + case CH_NUMERIC: + state = s_num; + s_num(ch); + break; + case CH_ALPHA: + state = s_word; + s_word(ch); + break; + case CH_ESCAPE: + state = s_leadcmd; + break; + case CH_SPACE: + spo_pause(P_WORD); + break; + case CH_QUOTE: + if(quote) + { + spo_word("end"); + quote = FALSE; + } + else + quote = TRUE; + spo_word("quote"); + break; + default: + switch(ch) + { + case '$': + word[0] = '$'; + cpos = 1; + state = s_num; + break; + case '*': + spo_word("times"); + break; + case '/': + spo_word("divided"); + spo_word("by"); + break; + case '+': + spo_word("plus"); + break; + case '-': + spo_word("minus"); + break; + case '&': + spo_word("and"); + break; + case '|': + spo_word("or"); + break; + case '~': + spo_word("not"); + break; + case ')': + spo_word("end"); + case '(': + spo_word("parenthesis"); + break; + case '=': + spo_word("equals"); + break; + case '<': + spo_word("less"); + spo_word("than"); + break; + case '>': + spo_word("greater"); + spo_word("than"); + break; + default: + spo_pause(P_WORD); + } + } +} + +void chars(FILE *fp) +{ + int i, ch; + + quote = FALSE; + state = s_idle; + + if(!init) + { + for(i = 0; i < 256; ++i) + map[i] = CH_IGNORE; + + map[27] = CH_ESCAPE; + + for(i = 33; i < 127; ++i) + map[i] = CH_PUNCT; + + map[9] = map[13] = map[10] = map[32] = CH_SPACE; + for(i = 48; i < 58; ++i) + map[i] = CH_NUMERIC; + + for(i = 0; i < 26; ++i) + { + map[i + 65] = CH_ALPHA; + map[i + 97] = CH_ALPHA; + } + map['.'] = CH_PERIOD; + map['\"'] = CH_QUOTE; + } + + while(EOF != (ch = fgetc(fp))) + (*state)(ch); + + (*state)(0); +} diff --git a/spo256/client.c b/spo256/client.c new file mode 100644 index 0000000..b43696f --- /dev/null +++ b/spo256/client.c @@ -0,0 +1,40 @@ +/* + * Client operating mode. + * $Id: client.c 1.2 Mon, 24 Mar 1997 12:25:37 -0500 dyfet $ + * Copyright (c) 1997 by Tycho Softworks. + * For conditions of distribution and reuse see product license. + * + * Abstract: + * The client mode of operation will connect to SPO256 server and + * redirect standard output to the server. This allows the speak + * (spo256) image to be used to terminate pipes. + */ + +#include +#include +#include +#include +#include +#include +#include "speak.h" + +void client(char *hostname, ushort port) +{ + STREAM fp = opentcp(hostname, port); + char buf[1024]; + + if(!fp) + fatal(EX_UNAVAILABLE, "spo256: %s: unkown host\n", hostname); + + gettcp(buf, sizeof(buf) - 1, fp); /* get banner line */ + while(!feof(stdin)) + { + fgets(buf, sizeof(buf) - 1, stdin); + if(feof(stdin)) + break; + + puttcp(buf, fp); + } + closetcp(fp); + exit(0); +} diff --git a/spo256/currency.c b/spo256/currency.c new file mode 100644 index 0000000..b79c036 --- /dev/null +++ b/spo256/currency.c @@ -0,0 +1,43 @@ +/* + * Currency rules, for USD. + * $Id: currency.c 1.2 Mon, 24 Mar 1997 12:25:37 -0500 dyfet $ + * Copyright (c) 1997 by Tycho Softworks. + * For conditions of distribution and reuse see product license. + */ + +#include +#include +#include "speak.h" + +void currency(char *str) +{ + char *cents; + ++str; + + cents = strchr(str, '.'); + if(cents) + *(cents++) = 0; + + if(atol(str) != 0) + { + number(str); + if(atol(str) > 1) + spo_word("dollars"); + else + spo_word("dollar"); + } + + if(cents) + { + if(atol(str) > 0) + spo_word("and"); + + number(cents); + if(atoi(cents) > 1) + spo_word("cent"); + else + spo_word("cents"); + } +} + + diff --git a/spo256/getidx.c b/spo256/getidx.c new file mode 100644 index 0000000..70a730c --- /dev/null +++ b/spo256/getidx.c @@ -0,0 +1,98 @@ +/* + * Build word and abbreviation index in memory from .conf. + * $Id: getidx.c 1.2 Mon, 24 Mar 1997 12:25:37 -0500 dyfet $ + * Copyright (c) 1997 by Tycho Softworks. + * For conditions of distribution and reuse see product license. + */ + +#include +#include +#include +#include +#include +#include "speak.h" + +MEMPOOL *mem; +IDX **widx, **aidx; + +static int key(uchar *str) +{ + unsigned int k = 0; + int len = strlen((char *)str); + + while(*str) + { + k = (k * 7) | (*str & 0x1f); + ++str; + } + + k |= len; + + return (k % KEYSIZE); +} + +void getidx(CONFIG *cfg) +{ + int i; + char *p, *q; + IDX *new; + + mem = mempool(16384, 16); + + widx = (IDX **)memreq(mem, sizeof(IDX *) * KEYSIZE); + aidx = (IDX **)memreq(mem, sizeof(IDX *) * KEYSIZE); + + + for(i = 0; i < KEYSIZE; ++i) + widx[i] = aidx[i] = NULL; + + seek_config(cfg, "words"); + while(NULL != (p = read_config(cfg))) + { + q = strchr(p, '='); + if(!q) + continue; + + *(q++) = 0; + p = strtrim(p, __SPACES); + q = strtrim(q, __SPACES); + i = key((uchar *)p); + new = memlreq(mem, sizeof(IDX)); + new->word_link = widx[i]; + widx[i] = new; + new->word_key = strreq(mem, p); + new->word_spo = strreq(mem, q); + } + seek_config(cfg, "abbrev"); + while(NULL != (p = read_config(cfg))) + { + q = strchr(p, '='); + if(!q) + continue; + + *(q++) = 0; + p = strtrim(p, __SPACES); + q = strtrim(q, __SPACES); + i = key((uchar *)p); + new = memlreq(mem, sizeof(IDX)); + new->word_link = aidx[i]; + aidx[i] = new; + new->word_key = strreq(mem, p); + new->word_spo = strreq(mem, q); + } +} + +char *find(IDX **table, char *str) +{ + IDX *next = table[key((uchar *)str)]; + + + while(next) + { + if(!stricmp(str, next->word_key)) + return next->word_spo; + + next = next->word_link; + } + return NULL; +} diff --git a/spo256/getspo.c b/spo256/getspo.c new file mode 100644 index 0000000..5ff64d7 --- /dev/null +++ b/spo256/getspo.c @@ -0,0 +1,68 @@ +/* + * Parse .conf file interface spec for spo communications. + * $Id: getspo.c 1.2 Mon, 24 Mar 1997 12:25:37 -0500 dyfet $ + * Copyright (c) 1997 by Tycho Softworks. + * For conditions of distribution and reuse see product license. + */ + +#include +#include +#include +#include +#include + +char *mask = NULL; + +int getspo(CONFIG *cfg) +{ + int spo = -1; + char *p; + stty_t stty; + + if(!seek_config(cfg, "interface")) + fatal(EX_CONFIG, "spo256: [interface]: missing from speak.conf\n"); + + while(NULL != read_config(cfg)) + { + if(NULL != (p = get_config(cfg, "bind"))) + { + mask = strdup(p); + continue; + } + + if(NULL != (p = get_config(cfg, "device"))) + { + spo = open(p, O_RDWR); + if(spo < 0) + fatal(EX_UNAVAILABLE, "spo256: %s: cannot access\n", p); + stty = getstty(spo); + interactive(stty, FALSE); + setflowctrl(stty, FC_HARD); + continue; + } + + if(NULL != (p = get_config(cfg, "speed"))) + { + if(spo < 0) + fatal(EX_CONFIG, "spo256: set speed for unspecified device\n"); + setspeed(stty, atol(p)); + continue; + } + + if(NULL != (p = get_config(cfg, "parity"))) + { + if(spo < 0) + fatal(EX_CONFIG, "spo256: set format for unspecified device\n"); + if(!stricmp(p, "even")) + setformat(stty, "7e1"); + if(!stricmp(p, "odd")) + setformat(stty, "7o1"); + if(!stricmp(p, "none")) + setformat(stty, "8n1"); + continue; + } + } + putstty(spo, stty, TRUE); + return spo; +} + diff --git a/spo256/number.c b/spo256/number.c new file mode 100644 index 0000000..16c662a --- /dev/null +++ b/spo256/number.c @@ -0,0 +1,132 @@ +/* + * Number base pronounciation rules and special cases. + * $Id: number.c 1.2 Mon, 24 Mar 1997 12:25:37 -0500 dyfet $ + * Copyright (c) 1997 by Tycho Softworks. + * For conditions of distribution and reuse see product license. + */ + +#include +#include +#include "speak.h" + +static char *nteen[] = { + "ten", "eleven", "twelve", "thirteen", "fourteen", + "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"}; + +static char *n10[] = { + "zero", "ten", "twenty", "thirty", "fourty", + "fifty", "sixty", "seventy", "eighty", "ninety"}; + + +static void digits(long value) +{ + bool hundred = FALSE; + char buf[2]; + + if(value >= 100) + { + digits(value / 100); + spo_word("hundred"); + value %= 100; + hundred = TRUE; + } + + if(!value && hundred) + return; + + if(value >= 20) + { + spo_word(n10[value / 10]); + value %= 10; + if(!value) + return; + } + + if(value < 10) + { + buf[0] = value + '0'; + buf[1] = 0; + spo_word(buf); + return; + } + spo_word(nteen[value - 10]); +} + +void number(char *str) +{ + char *p; + long value = atol(str); + + if(lit || spell) + { + spo_word(str); + return; + } + + /* Check for w.x.y.z as "w dot x dot y dot z" */ + + if(ccount(str, ".") > 1) + { + while(NULL != (p = strchr(str, '.'))) + { + *(p++) = 0; + number(str); + str = p; + } + number(str); + return; + } + + if(NULL != (p = strchr(str, '.'))) + { + *(p++) = 0; + number(str); + spo_word("point"); + spo_word(p); + return; + } + + if(NULL != (p = strchr(str, ':'))) + { + digits(atol(str)); + ++p; + if(atol(p) < 10) + if(atol(p)) + write(spo, " O ", 2); + + if(atol(p)) + digits(atol(p)); + + if(NULL != (p = strchr(p, ':'))) + { + spo_word("and"); + digits(atol(++p)); + spo_word("seconds"); + } + else + { + p = str; + if(strchr(p, 'a') || strchr(p, 'A')) + write(spo, "A M ", 5); + if(strchr(p, 'p') || strchr(p, 'P')) + write(spo, "P M ", 5); + } + return; + } + + if(value >= 1000000l) + { + digits(value / 1000000l); + spo_word("million"); + value %= 1000000l; + } + + if(value >= 1000) + { + digits(value / 1000); + spo_word("thousand"); + value %= 1000; + } + + digits(value); +} diff --git a/spo256/speak.1 b/spo256/speak.1 new file mode 100644 index 0000000..8ceac59 --- /dev/null +++ b/spo256/speak.1 @@ -0,0 +1,46 @@ +.TH "speak" "1" "January 1997" "Speak 0.2" "Text-to-Speech" +.PP +.SH NAME +.PP +\fBspeak\fP - send output to text-to-speech service. +.PP +.SH SYNOPSIS +.PP +\fBspeak\fP [\fIhostname\fP] +.PP +.SH DESCRIPTION +.PP +The Text-to-speech package introduces a standard TCP/IP port service for +attaching text-to-speech resources to your network. These resources can +be used to monitor system status, provide verbal alarms, etc. This network +service may be accessed by telnetting to a machine running a "speak" server, +or by using the various application utilities, such as \fBspeak\fP. +.PP + +The \fBspeak\fP program will capture and redirect all standard input to the +network speech server, such as \fBspo256\fP, running either on the +local machine ("\fIlocalhost\fP"), or to the server running on the host +specified as a command line argument. \fBspeak\fP is normally used to +terminate a pipe, such as in "\fIcat filename | speak\fP". The TCP service +port used for the connection is specified in \fI/etc/services\fP. + +.PP +While most of the text-to-speech programs are separately written, the +\fBspeak\fP program is normally an alias for the actual server itself, +such as the \fBspo256\fP program. This was intentional, as future +versions of speak may drive hardware directly in an alternate usage +(such as "\fIspeak /dev/ttyxx\fP"), as well as redirecting output to a named +host. + +.PP +.SH SEE ALSO +.PP +.BR say (1), +.BR down (8), +.BR spo256 (8), +.BR services (7) +.PP +.SH AUTHOR +.PP +David Sugar (dyfet@tycho.com) +.PP diff --git a/spo256/speak.8 b/spo256/speak.8 new file mode 100644 index 0000000..aaeceef --- /dev/null +++ b/spo256/speak.8 @@ -0,0 +1,39 @@ +.TH "spo256" "8" "1997" "Text-to-Speech services" "Tycho Softworks" +.PP +.SH "NAME spo256 \- SPO256-AL2 Text-to-Speech Server" +.PP +.SH "SYNOPSIS" +.PP +\fBspo256\fP +.br +\fBspeak\fP [hostname] +.PP +.SH "DESCRIPTION" +.PP +.SH "FILES" +.PP +The spo256 server configuration may be found in \fB/etc/speak.conf\fP. +.PP +.SH "SEE ALSO" +.PP +.IP +.IP o +\fBdown(8)\fP: Shuts down the system. +.IP +.IP o +\fBsay(1)\fP: Sends output to a "speak" server. +.IP +.IP o +\fBservices(5)\fP: Installed TCP/IP network services. +.IP +.PP +.SH "DIAGNOSTICS" +.PP +\fBspo256\fP will warn and fail to load if the "speak" services entry +is missing, if the device cannot be opened, or if the speak.conf file +is missing. +.PP +.SH "AUTHOR" +.PP +David Sugar (dyfet@tycho.com) +.PP diff --git a/spo256/speak.c b/spo256/speak.c new file mode 100644 index 0000000..7de099f --- /dev/null +++ b/spo256/speak.c @@ -0,0 +1,86 @@ +/* + * Base (main) for spo server. + * $Id: speak.c 1.2 Mon, 24 Mar 1997 12:25:37 -0500 dyfet $ + * Copyright (c) 1997 by Tycho Softworks. + * For conditions of distribution and reuse see product license. + * + * Abstract: + * Establish basic operating environment for SPO256-AL2 text-to-speech + * server. This module verifies system configuration, parses the + * config files for word lists that are stored in the quick-lookup + * internal hash word subsitution database, binds the server to a tcp + * network port, and accepts client requests. + */ + +#include +#include +#include +#include +#include +#include +#include "speak.h" + +int spo; /* device id of spo */ +FILE *io; + +void main(int argc, char **argv) +{ + int port = getservice("speak"); + CONFIG *cfg; + SOCKET so; + char buf[1024]; + + if(!port) + fatal(EX_UNAVAILABLE, "spo256: speak: service not defined in /etc/services\n"); + + if(argc < 1 || argc > 2) + fatal(EX_USAGE, "use: spo256 [hostname]\n"); + + if(argc > 1) + client(argv[1], port); + + if(!stricmp(pathfname(argv[0]), "speak")) + client("localhost", port); + + if(NULL == (cfg = sys_config("speak"))) + fatal(EX_CONFIG, "spo256: speak.conf: config file missing\n"); + + /* get device configuration */ + + spo = getspo(cfg); + + getidx(cfg); + close_config(cfg); + + /* bind server to socket */ + + so = tcpsocket(mask, port, 5); + if(so == INVALID_SOCKET) + fatal(EX_UNAVAILABLE, "spo256: %d: failed to bind port\n", port); + + /* now, make ourselves a daemon */ + + pdetach(D_KEEPNONIO); + + /* Main loop of server. The server accepts one connection in + turn from each requesting user application. The remaining + application requests are kept in backlog while the current + application is served. This assures non-overlapping speech. + */ + + for(;;) + { + io = accepttcp(so); + if(!io) + continue; + + spo_init(); + puttcp("SPO256-AL2 Text-to-Speech Server 1.0\r\n", io); + + chars(io); + + spo_pause(P_END); + closetcp(io); + } +} + diff --git a/spo256/speak.conf.7 b/spo256/speak.conf.7 new file mode 100644 index 0000000..ccc3e57 --- /dev/null +++ b/spo256/speak.conf.7 @@ -0,0 +1,72 @@ +.TH "speak.conf" "7" "January 1997" "Speak 0.2" "Text-to-Speech" +.PP +.SH NAME +.PP +\fBspeak.conf\fP - configuration file for text-to-speech services. +.PP +.SH SYNOPSIS +.PP +\fBspeak.conf\fP +.PP +.SH DESCRIPTION +.PP +The \fBspeak.conf\fP file is a text edit-able configuration file used by the +text-to-speech server and various text-to-speech utilities. +.PP +\fBspeak.conf\fP is divided into sections, donated with "[]"'s. These +sections include hardware parameters for the text-to-speech server used on the +local machine, translation tables used for phonetic dictionaries by text-to- +speech servers, and runtime default information for specific programs. On +machines that do not run their own text-to-speech server, \fBspeak.conf\fP +will still be used to specify runtime defaults for utility programs that are +present. +.PP +.SH FILE FORMAT +.PP +The file consists of sections and parameters. A section begins with the +name of the section in square brackets and continues until the next section +begins. Sections contain parameters of the form \'name = value`'. +.PP +The file is line-based text - that is, each newline-terminated line +represents either a comment, a section name, or a parameter. Parameters +in the .conf file normally do not appear on multiple lines, except for +several specific exceptions (lists and tables) that are not used in this +package. +.PP +Section and parameter names are not case sensitive. +.PP +Only the first equal sign in a parameter is significant. Whitepsaces before +or after the = are ignored. Leading and trailing whitespaces are ignored. +Whitepaces within a parameter name are also ignored, while whitespaces +within a parameter value are preserved. +.PP +Any line beginning with a semicolon or a pound sign is treated as a comment +and is ignored. +.PP +Any line containing a list, in the form 'name = { v1 v2 v3 }' may be +continued over multiple lines if the terminating '}' is not found in the +current line. +.PP +Any line beginning with a plus is considered a repeat of the "name =" portion +of the last parameter line. This is often used to build up hostname lists +when multiple hostnames may be specified. +.PP +The parameter value may either be quoted or unquoted. Quoted parameters +values may be used to specify lead or trailing spaces which would otherwise +be ignored. Either matching single or double quotes may be used, and the +quotes are not kept as part of the value returned to the application. +.PP +Parameter values may either be strings, numeric values, or boolean flags. +When a boolean parameter is used, the characters "T" for 'true' and "F" for +'false' may be used. The full word, "true" and "false", may also be +spelled out. "Y" or "Yes" and "N" or "No" may also be used for "true" or +"false". +.PP +.SH SEE ALSO +.PP +.BR speak (1) +.PP +.SH AUTHOR +.PP +David Sugar (dyfet@tycho.com) +.PP diff --git a/spo256/speak.h b/spo256/speak.h new file mode 100644 index 0000000..07b9875 --- /dev/null +++ b/spo256/speak.h @@ -0,0 +1,48 @@ +/* + * Master header for spo server. + * $Id$ + * Copyright (c) 1997 by Tycho Softworks. + * For conditions of distribution and use see license. + * + * Abstract: + * Define internal hash index system to quickly find text for + * substitutions. The spo-256 is driven by substitution tables + * which provide alternate spellings to adjust mis-pronounced + * speech. + */ + +typedef enum +{ + P_END, /* Finish line off, send SPO-end mark */ + P_LINE, /* Send nl because we have a empty line */ + P_SENTANCE, /* Pause at end of sentance (nl) */ + P_WORD /* Inter-word delay (spaces) */ +} PAUSE; + +#define KEYSIZE 791 /* Size of internal hash database keyrange */ + +typedef struct _idx +{ + struct _idx *word_link; /* hash link chain */ + char *word_key; /* initial word & hash key */ + char *word_spo; /* substitute spelling */ +} IDX; + +extern int spo; +extern IDX **widx, **aidx; +extern char *mask; +extern FILE *io; +extern bool echo, spell, lit; + +void abbrev(char *str); +int getspo(CONFIG *cfg); +void getidx(CONFIG *cfg); +char *find(IDX **table, char *str); +void client(char *hostname, ushort port); + +void spo_init(void); +void spo_cmd(char *str); +void spo_word(char *str); +void spo_pause(PAUSE); +void spo_begquote(void); +void spo_endquote(void); diff --git a/spo256/speak.init b/spo256/speak.init new file mode 100644 index 0000000..0c46b8a --- /dev/null +++ b/spo256/speak.init @@ -0,0 +1,54 @@ +#!/bin/sh + +# modify FILESYSTEMS to activate and use vmon. +FILESYSTEMS="/" + +mode=$1 + +if [ $# = 0 ] ; then + mode='rc' +fi + +if [ ! "$mode" = 'rc' ] ; then + # Source function library. + . /etc/rc.d/init.d/functions + + # Source networking configuration. + . /etc/sysconfig/network + + # Check that networking is up. + [ ${NETWORKING} = "no" ] && exit 0 +fi + +# See how we were called. +case "$mode" in + rc) + echo "Starting SPO256-AL2 server" + spo256 + if [ ! -z "$FILESYSTEMS" ] ; then + vmon $FILESYSTEMS + fi + ;; + start) + echo -n "Starting SPO256-AL2 speech services: " + daemon spo256 + if [ ! -z "$FILESYSTEMS" ] ; then + daemon vmon $FILESYSTEMS + fi + echo + touch /var/lock/subsys/speak + ;; + stop) + echo -n "Shutting down SPO256-AL2 speech services: " + killproc spo256 + if [ ! -z "$FILESYSTEMS" ] ; then + killproc vmon + fi + rm -f /var/lock/subsys/speak + echo "" + ;; + *) + echo "Usage: speak.init {start|stop}" + exit 1 +esac + diff --git a/spo256/spo.c b/spo256/spo.c new file mode 100644 index 0000000..5c4f7ec --- /dev/null +++ b/spo256/spo.c @@ -0,0 +1,161 @@ +/* + * SPO256 low level operations. + * $Id: spo.c 1.2 Mon, 24 Mar 1997 12:25:37 -0500 dyfet $ + * Copyright (c) 1997 by Tycho Softworks. + * For conditions on distribution and reuse see product license. + * + * Abstract: + * Key SPO translation modules. This includes the word substution + * module, the numeric pronounciation system, pause control, and + * other low level spo services. + */ + +#include +#include +#include "speak.h" + +/* + * Flags and operating mode properties that may now be effected by + * server commands. + */ + +bool echo = FALSE; /* echo SPO output to user? */ +bool spell = FALSE; /* spell all words? */ +bool lit = FALSE; /* literally pronounce as is? */ + +/* + * A new 'init' function is added to initialize operating state for + * each new connection. This assures that spo command sequences + * issued from a previous session are not left in effect. + */ + +void spo_init(void) +{ + echo = FALSE; /* echo off by default */ + spell = FALSE; + lit = FALSE; +}; + +/* + * Interpret server specific commands embedded in + * sequences. The SPO has limited options to effect in this manner. + */ + +void spo_cmd(char *str) +{ + if(!stricmp(str, "echo")) + { + echo = TRUE; + return; + } + + if(!stricmp(str, "/echo")) + { + echo = FALSE; + return; + } + + if(!stricmp(str, "spell")) + { + spell = TRUE; + return; + } + + if(!stricmp(str, "/spell")) + { + spell = FALSE; + return; + } + + if(!stricmp(str, "lit")) + { + lit = TRUE; + return; + } + + if(!stricmp(str, "/lit")) + { + lit = FALSE; + return; + } +}; + +/* + * Pause spo output between words, numbers, and empty lines, so that + * words are properly announciated. This is typically accomplished with + * spaces and newline control. + */ + +void spo_pause(PAUSE p) +{ + switch(p) + { + case P_WORD: + write(spo, " ", 1); + if(echo) + puttcp(" ", io); + break; + case P_SENTANCE: + write(spo, " \n", 2); + if(echo) + puttcp(" \n", io); + break; + case P_LINE: + write(spo, " \r\n", 3); + if(echo) + puttcp(" \r\n", io); + break; + case P_END: + write(spo, "\r\n", 2); + } +} + +/* Announce quoted text. */ + +void spo_begquote(void) +{ + spo_word("quote"); +} + +void spo_endquote(void) +{ + spo_word("end"); + spo_begquote(); +} + +/* + * This performs low level lookup and pronounciation of words, either + * in the originally submitted spelling, or in the spo dictionary + * spelling. + */ + +void spo_word(char *word) +{ + char *p = find(widx, word); + if(!p) + p = word; + + if(lit) + p = word; + + if(spell) + { + p = word; + while(*p) + { + write(spo, p, 1); + if(echo) + fputc(*p, io); + spo_pause(P_WORD); + ++p; + } + } + else + { + write(spo, p, strlen(p)); + if(echo) + puttcp(p, io); + } + spo_pause(P_WORD); +} + diff --git a/spo256/spo256.8 b/spo256/spo256.8 new file mode 100644 index 0000000..a0826ad --- /dev/null +++ b/spo256/spo256.8 @@ -0,0 +1,98 @@ +.TH "spo256" "8" "January 1997" "Speak 0.2" "Text-to-Speech" +.PP +.SH NAME +.PP +\fBspo256\fP - text-to-speech server for SPO256-AL2 chipset. +.PP +.SH SYNOPSIS +.PP +\fBspo256\fP +.PP +.SH DESCRIPTION +.PP +The \fBspo256\fP server provides a TCP/IP service interface for controlling +a serial based SPO256-AL2 text-to-speech chip. The server binds to the +"\fIspeak\fP" port defined in /etc/services and attaches to the serial +tty device specified in the [\fIinterface\fP] section of \fBspeak.conf\fP. +The server accepts a single TCP session from a client application at a time, +streaming that client's text to the SPO device. The server corrects +pronunciation of words, numbers, and common usages by performing in-stream +text substitution before sending output to the SPO. +.PP +.SH INSTALLATION +.PP +The \fBspo256\fP server is meant to be started during system initialization +(usually for run level 3). A \fBspeak.init\fP script is installed into +/etc/rc.d, and this script will be linked to a numbered phase in the +/etc/rc3.d directory. This final linkage is now part of the 'make install' +supplied and should work for Linux, and perhaps other UNIX systems with +similar rc.d directory and file layouts. The \fBspo256\fP service can +normally start immediately after network services (\fIS10network\fP) have ran. +.PP +For BSD style systems, the speak.init script is installed as 'rc.speak'. For +systems that have different naming schemes for the rc.d scripts, some +additional work may be needed to correctly install speak.init. The +\fBspo256\fP server may also be ran directly from the command line For +testing, and the \fBspo256\fP command can simply be added to the +\fIrc.local\fP file. +.PP +The make install will add an entry to \fB/etc/services\fP for the TCP speak +port. By default, I normally use port 800. Since no effort has been made +to register text-to-speech as a defined Internet port service, it is quite +conceivable port 800 may at some point be defined for another service. If +this happens, simply edit the /etc/services file as needed. +.PP +The [\fIinterface\fP] section of \fB/etc/speak.conf\fP will need to be +edited before you start \fBspo256\fP for the first time. The entry for +\fIdevice\fP should be given the correct tty serial port where your SPO +board is attached. The speed I choose to use is 2400bps, which is the +maximum speed the board is capable of. Valid values for parity include +"odd", "even", and "none". Also verify board jumpers +are set for the speed and data format specified in [\fIinterface\fP]. +.PP +.SH "CORRECTIVE SPEECH" +.PP +The [\fIwords\fP] section of \fBspeak.conf\fP holds a list of text +substitutions. These substitutions correct the inability of the SPO to +correctly pronounce may words. Since the SPO provides no direct access +to the low-level phonetic dictionary, these substitute spellings may +appear odd. Sometimes, words are broken into multiple words with spaces, +such as "online", which is sent to the SPO as "on lyne". Basically, +through trial and error, one can build up a substitution table that works +for the device. +.PP +The [\fIabbrev\fP] section simply provides the meaning behind commonly +used abbreviations, so that they are spoken out correctly. Abbreviations +are presumed to terminate with a '.', such as "Dr.", "Mr.", and are +represented in a table separate from the text substitution table. +.PP +.SH SERVER COMMANDS +.PP +Server commands were introduced with the third release of the SPO256 +server. These commands are formed as "tags" which appear as part of the +output stream sent to the server. These tags are preceeded by the +character, and are held in <>'s (much like HTML tags). Server commands +that are supported by different speech servers may very widly to support +features such as volume level, tone, inflection, etc. The limited set +of commands supported by the SPO server are as follows: +.PP +The <\fIecho\fP> and <\fI/echo\fP> tags may be used to turn echo on and +off. When echo is enabled, the output that is actually sent to the SPO +device (including corrected spellings) is also echo'd back to the client +that is connected to the server. +.PP +The <\fIspell\fP> and <\fI/spell\fP> tags may be used to turn spelling of +words on and off. In spelling mode, each letter of each word is spoken. +This should not be confused with <\fIlit\fP> and <\fI/lit\fP>, which are +used to disable the word lookup function so that text as actually sent +to the server is sent unprocessed to the SPO board. +.PP +.SH SEE ALSO +.PP +.BR speak.conf (7), +.BR services (7) +.PP +.SH AUTHOR +.PP +David Sugar (dyfet@tycho.com) +.PP diff --git a/spo256/word.c b/spo256/word.c new file mode 100644 index 0000000..2150837 --- /dev/null +++ b/spo256/word.c @@ -0,0 +1,38 @@ +/* + + Word base pronounciation rules and special cases. +*/ + +#include +#include +#include +#include +#include "speak.h" + +void word(char *str) +{ + char *p; + + /* Internet x@y as "x at y" */ + + if(NULL != (p = strchr(str, '@'))) + { + *(p++) = 0; + word(str); + word("at"); + word(p); + return; + } + + /* Check for x.y.z as "x dot y dot z" */ + + while(NULL != (p = strchr(str, '.'))) + { + *(p++) = 0; + spo_word(str); + spo_word("dot"); + str = p; + } + + spo_word(str); +} diff --git a/spo256/words.c b/spo256/words.c new file mode 100644 index 0000000..19c8d57 --- /dev/null +++ b/spo256/words.c @@ -0,0 +1,34 @@ +/* + + Each line of input from the client application is examined and split + into words, numbers, abbreviations, and special catagories for further + translation from free-form text to SPO spoken speech. Generally a + word is a collection of characters grouped by white-space markers + (nl, tab, and space). Special consideration is also given to quoting + properties. Additional rules may be applied once words have been + catagorized. +*/ + +#include +#include +#include +#include "speak.h" + +void abbrev(char *str) +{ + char *p; + + if(!lit && !spell) + p = find(aidx, str); + + if(p) + spo_word(p); + else + { + p = tail(str); + *(--p) = 0; + spo_word(str); + spo_pause(P_SENTANCE); + } +} + -- cgit v1.2.3-54-g00ecf