# /* Voice Synthesizer Program "Speak" */ /* */ /* Language: C */ /* Programmer: M. D. McIlroy */ /* */ /* For description of method see "Synthetic English */ /* Speech by Rule", M. D. McIlroy, Bell Telephone */ /* Laboratories, Inc. 1974 */ /* modified for QMC synthesiser */ /* George Coulouris 7 June 76 */ /* further alterations sept 76 j.d. for spout ****j*** */ #define NT 800 #define NS 9500 # define FLAG "" # define BELL 07 #define NRULE 20 #define NETMODE 042 /*raw no echo, no mappings*/ # define CR 015 /*term for spout*/ char *diags[] { 0, "bad option", "no vocabulary", "can't create", "too many words", "too many chars" }; char *aeiou "aeiou"; char *aeiouy "aeiouy"; char *aeiouwxy "aeiouwxy"; char *aeo "aeo"; char *aou "aou"; char *bcdfgkpt "bcdfgkpt"; char *sendch "\n\r \t"; #define USRBRK 02000 #define ALL8 04000 #define NORMAL 0332 #define ITT 0100000 int savetty, savebrks[8]; struct mode { char m_inspeed; char m_outspeed; char m_erase; char m_kill; int m_flags; char m_nldelay; char m_crdelay; char m_tbdelay; char m_vtdelay; char m_width; char m_length; int m_brk[8]; } tty; setbreak() { register char *sp; int i; if(gterm(0,&tty) != -1) { savetty = tty.m_flags; for (i = 0; i<8; i++) savebrks[i] = tty.m_brk[i]; tty.m_flags =| USRBRK; sp = sendch; do setbit(&tty.m_brk[0], *sp) ;while(*++sp); sterm(0, &tty); } } setcook() { int i; tty.m_flags = savetty; for(i = 0; i<8; i++) tty.m_brk[i] = savebrks[i]; sterm(0, &tty); exit(); } struct rec { int word,phon; }; int recsize 4; struct rec table[NT]; /*this table is an index to strings itgives the address of each word and each*/ /*phoneme in strings.*/ char strings[NS]; /*The index and the strings are read in from a file eng.m at the start of */ /* the program the strings are of the form */ /* 0'%''#'010 to represent ' ''%''#''cr'',''%''cr' 001 replaces % on rhs*/ /* 0'%''#''U''n'01'u''n' to represent '%''#''U''n''cr'',''%'u''n' */ /* 0'%''#''U''n''I'01'U''n''i'0 for ' ''%''#''U''n''I''cr'',''%''U''n''i' */ /* and so forth*/ /*The table entries for this are 0 0 1 4 6 11 etc*/ /* to make this file di : speak -v null1 && *argv[1]=='-') { loop: switch(*(argv[1]++)) { default: goto loop; case 'e': eflag = 0; /*no english preprocessing*/ goto loop; case 'f': for(i=1;i2) vs= creat(argv[2],0666); } readin(argc>1 && compare(argv[1], ",") ? argv[1]:"/usr/lib/eng.m"); /* readin ( file name for coded vocabulary) */ /* file name if argc>1 and argv[1] not , use name in arg */ /* otherwise use /usr/lib/speak.m */ if (vflag) write(vs,"l", 4); /*3 bells and l for spout **j** */ for(;;) { t = line; do { if(!read(0,t,1)) { if(vflag) write(vs,FLAG,1); /*send term to spout */ if(uflag) setcook(); else exit(); } } /*text is input to 'line' until term charac is found this is replaced by zero*/ while( uflag ? !oneof( *t++,sendch) : *t++ != '\n' ); if(t-line<2)continue; thisterm= *(t-1); *(t-1)=' '; *t=0; if(line[0]=='!') switch(line[1]) { /*spec cases intr ! */ case 'c': copy(); break; case 'w': writeout(name()); /*write compressed file*/ case 'r': t = name(); /* reads vocab from named file*/ if(*t) readin(t); break; case 'd': if(phread(work,buf+1)!=buf+1) decode(1,buf+1); else { tflag = 1; phpron(work,buf); tflag = 0; } write(2,"\n",1); break; case 'f': t = name(); for(i=0;*t>='0'&&*t<='9'; i = i*10 + *t++-'0'); if(i>=NRULE) break; rulesw[i] =^ 1; break; case 'p': decode(2,buf+1); write(2,"\n",1); break; case 'q': if (vflag) write(vs,FLAG,1); if(uflag) setcook(); else exit(); case 'l': i = 0; /*list vocab*/ while(++i=*u ||*u=='%') lflag++; u++; } else goto next; } *u++ = 0; } /*by now the string has been copied to 'work' with out any blanks*/ /*letterbcount to lflag*/ next: wtop = u; *m=0; } if(u==work) continue; t = work; if(bflag){ u=phspell(t,buf+1); bflag=0; goto worddone; } m=markers; if(mflag&&dflag)while(*m)write(phonfil,m++,1); m++; while(t=2 &&w[-1]!='\b'){ wflag = *w; *w = 0; break; } /*hyphen taht cuts off 2 or more characters treated as space*/ if(!wflag){ for(;oneof(*--w,".,;:?!'\"])");); if(*w=='\b') w++; yflag = *++w; } /*trailing punctuation causes pause*/ if(w<=v) goto noword; *w = 0; xflag = 0; if(!lflag) for(u=v;*u;u++) if(fold(u)) xflag++; /*map uc to lc and remember*/ if((yflag||xflag||wflag)&& (u=phread(v,buf+1))!=buf+1); /*if uc mapped or punctuation stripped try the dictionary again*/ else u = phpron(v,buf+1); if((*w=yflag)&&u!=buf+1) { /*pause for punct*/ *u++ = 040; *u++ = 040; } } noword: if(u==buf+1&&wflag) *w = wflag; if(u==buf+1&&sflag) u = phspell(t,buf+1); *buf = 040; /*pause before word*/ worddone: *u++ = 040; /*pause for end of word*/ *u++= 0; /*to stopdecode*/ while(*t++); if(dflag) decode (phonfil,buf+1); if(mflag&&dflag) while(*m)write(phonfil,m++,1); m++; if(vflag) { qbuft=qencode(buf,qbuf); /* returns no of characters in qbuf */ write(vs,qbuf, qbuft- qbuf ); } }/*goes back after each word in non replacement lines*/ if(dflag) write(phonfil, &thisterm,1); }/*end of lines not starting with ! */ }/*looping, reading lines, exit via !q only*/ }/*end of main*/ decode(f,s) char *s; { int b,c; int register flag; flag = 1; while(c = *s++) { if(flag) { write(f,",",1); if(c==001) { flag = 0; c = '%'; } else { c=-32; c= c ? code[c] : ' ' ; } } while(c){ write(f,&c,1); c=>>8; } } } /*xhoneme code (0-36 ) +32 with ) is source */ /* code and duration+32 is extracted from durcod*/ /*and sent in pairs to destination*/ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ } /* looks at test, if it is not intro by a comma return zero*/ /*this procedure deals with replacement lines in the making of the compressed*/ /*file if it cnsists of ,% write 1 string*/ /* if it is a replacement line put code*/ replace(){ char register *t,*u; int register n; int b,i; t = line; u = buf; if(*t++!=',')return(0); for(;;) { /*if comma*/ if(*t=='%') { *u++ = 001; while((*u = *++t) && *u!=' ') u++; break; /*if % get out here*/ } b = 1; if(*t<='3') if(*t>='0') b = '3'-*t++; n = 0; while(*t!=',' && *t!=' ' && *t!=0) { i = *t++; putchar(i); if(n) i=<<8; n =| i; } /* if , phoneme replace by code number*/ n = dencode(&code[0],n); if(n) *u++ = n; if(*t!=',' && *t!=' ') break; t++; } *u++=0; phwrite(work,buf); /*puts pair of items into compressed file*/ return(1); } list(f,s) char *s; { char register *t; if(phread(s,buf)==buf) return; write(f," ",1); t = s; while(*t) write(f,t++,1); write(f,"\n",1); decode(f,buf); write(f,"\n",1); } copy(){ char buf1[100]; phread(work,buf1); phwrite(name(),buf1); printf("stored in vocab as"); decode(2,buf+1); printf("\n"); } name(){ /* reads name from line[2] to buf*/ char register *u,*t; u = &line[2]; while(*u==' ') u++; t = buf; while(*u && (*t = *u++)!=' ') t++; *t = 0; printf("name %s \n",buf); return(buf); } readin(file){ int register f; if((f = open(file,0))<0) { diag(2); dflag=0; /*prevent display of phonemes*/ mflag=0; /*prevent removal of markers*/ /*this prevention is needed for making eng.m*/ return; } read(f,&ttop,2); read(f,table,recsize*ttop); read(f,&stop,2); read(f,strings,stop); close(f); } writo1(f,u,n) int *u; { int register i,j,k; i = j = *u; *u = n; k = 1; while(strings[i++]) k++; write(f,&strings[j],k); return(k); } writeout(file) { int register f,i; int n; if((f=creat(file,0666))<0) { diag(3); return; } printf("created %s %d\n",file,f); seek(f,recsize*ttop+4,0); /*get to byte 0 of string store*/ write(f,strings,1); /*and put it out*/ n = 1; for(i=1;ibot) { z = compare(in,&strings[table[i].word]); if(z==0) break; if(z<0) top = i; else bot = i; } return(i); } prefix(in) char *in; { char register *u,*s; char *end; int register i; int pref,bot,top; pref = bot = 0; top = ttop; end = in+1; /* +1 saves wasted time looking up % */ loop: while((i=(bot+top)/2)>bot) { do { s = &strings[table[i].word]; for(u=in;;u++) { if(*u<*s) top = i; else if(*u== *s++) { if(*u==0) return(i); if(*s!=0) continue; pref = bot = i; end = u+1; } else if(u<=end) bot = i; else break; goto loop; } } while((i=(bot+i)/2)>bot); bot++; end++; } return(pref); } compare(a,b) char *a,*b; { while(*a == *b) { if(*a==0) return(0); a++; b++; } return(*a<*b ? -1 : 1); } char * phread(in,out) /* returns address of letter after output string */ char *in,*out; { char *s; int i; i = find(in); if(compare(in,&strings[table[i].word])==0) { for(s = &strings[table[i].phon];*out = *s++;) out++; } *out = 0; return(out); } /*makes compressed file called by replace if comma in string*/ phwrite(in,out) char *in, *out; { int register i,j,z; i = find(in); if(0!=(z=compare(in,&strings[table[i].word]))) { if(*out==0) return; i++; if(ttop>=NT) { diag(4); return; } for(j=ttop;j>i;j--) { table[j].word = table[j-1].word; table[j].phon = table[j-1].phon; } /*move table down*/ table[i].word = stop; stash(in); ttop++; } /*copy lhs string*/ else if(*out==0) { for(j=i;j1?072:073; /*s=26 s=27*/ *out++ = 072; /*,s,s or ,z,s*/ goto done; } midu(t,s); finale(t,&s); mide(t,&s); mids(t,s); if(sflag) *++s = 's'; } *--t = '#'; *++s = '#'; *++s = 0; /*next section takes string from left to right applying pronunciation rules*/ /*to word fragents*/ while(*t) { *--t = '%'; i = prefix(t); /*looks for current string in table*/ if(i==0) { *sout = 0; return(sout); } u = &strings[table[i].word]; while(*u) { t++; if(tflag) write(2,u,1); /* decomposition of working word*/ u++; } if(tflag) write(2," ",1); s = &strings[table[i].phon]; while(*out = *s++) if(*out!=001) out++; else { /*do replacement*/ u = s; while(*u) u++; while(--u>=s) *--t = *u; break; } /*string has been put as code into buffer*/ } /*goes back with the l.h. end missing*/ done: *out = 0; return(out); spellit: for(u=t;u<=s;u++) if((*u|040)<'a'||(*u|040)>'z') goto done; return(phspell(t,out)); } char finals(in,ls) char *in,**ls; { char register *end; int *val; end = *ls; val = 0; if(!rulesw[1]) if(*end=='s'&&!oneof(end[-1],"us")) { *end-- = 0; if(*end=='\'') *end-- = 0; val = oneof(*end,"cfkpt")+1; } if(!rulesw[2]) if(*end=='e'&&end[-1]=='i') { *end-- = 0; *end = 'y'; } *ls = end; return(val); } midu(in,end) char *in,*end; { char register *s,*t; if(!rulesw[3]) for(s=in;s=in||*t=='h') { t = end; if(!rulesw[8]) while((u=suffix(in,t,suff1))!=t) { insert(u+1,ls); t = u; } if(!rulesw[9]) if((u=suffix(in,t,suff2))!=t) { insert(u+1,ls); return; } if(!rulesw[10]) if((u=suffix(in,t,suff3))!=t) { if(u[2]=='e') return; insert(u+1,ls); t = u; } if(!rulesw[13]) if(oneof(*t,"eiuy")&&vowel(in,t)=in) return; if(th(t+1)) { t[1] = 'T'; t[2] = 'H'; } } if((t==in||!oneof(t[-1],aeo)) && !(*t=='e'&&t[1]=='l')) *t =^ 040; } mide(in,ls) char *in,**ls; { char register *u,*end; end = *ls; for(u=in+3;uin+4 &&syltest(u+1,"aeiouy|",end) &&u[-1]=='l' &&oneof(u[-2],"bdfgkpt") &&oneof(u[-3],"bcdfgmnprst")) goto shift; if(!rulesw[15]) if(syltest(u+1,"aeinoruy|",end) &&!oneof(u[-1],"aehiouwxy") &&oneof(u[-2],"aiouyU") &&!oneof(u[-3],"aeiu")) { if(u[-3]!='o') u[-2] =& ~040; goto shift; } return; shift: insert(u+1,ls); } } mids(in,end) char *in,*end; { if(!rulesw[16]) while(++in=in;s--) s[1] = *s; *in = '|'; *ls = end; } char * suffix(in,end,s) char *in,*end,**s; { char register *t,*u; while(u = *s++) { t = end+1; while(*u == *--t) u++; if(*u==0) { if(vowel(in,t+1)=in) if(oneof(*end|040,aeiouy)) break; return(end); } fold(s) char *s; { if('A'<=*s && *s <='Z') { *s =^ 040; return(1); } return(0); } th(s) char *s; { return(*s=='t'&s[1]=='h'); }