SynthesizerImpl.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Polle Zellweger (PTZ) July 28, 1986 11:15:41 pm PDT
DIRECTORY
IO,
Rope USING [ Concat, Equal, Fetch, Length, ROPE, Substr ],
RuntimeError USING [ BoundsFault ],
Synthesizer,
VoiceUtils USING [ Report ]
;
SynthesizerImpl:
CEDAR PROGRAM
IMPORTS
Rope, RuntimeError, VoiceUtils
EXPORTS Synthesizer= {
This module is currently set up for the DECtalk. July 17, 1986 PTZ
BreakText:
PUBLIC
PROC [text: Rope.
ROPE, maxlen:
INT]
RETURNS [packet, remainder: Rope.
ROPE] = {
index: INT; insidePhonemes: BOOL;
IF text.Length < maxlen THEN RETURN[packet: text, remainder: ""];
[index, insidePhonemes] ← FindTextBreak[text, maxlen];
IF index=0
THEN {
index ← maxlen;
VoiceUtils.Report[Rope.Concat["Warning: No good place to break following string.\n", text.Substr[len: index]], $Finch];
};
packet ← text.Substr[len: index];
remainder ← text.Substr[start: index];
IF insidePhonemes
THEN {
-- close the current phoneme command, break, and reopen it
packet ← Rope.Concat[packet, "\033\\"];
remainder ← Rope.Concat["\033P;z", remainder];
};
};
FindTextBreak:
PROC [text: Rope.
ROPE, maxlen:
INT]
RETURNS [breakIndex:
INT, indexInsidePhonemes:
BOOL] = {
primary/secondaryBreak will be the first character of the next packet. Returns breakIndex = 0 if there is no good place to break the string.
cmdBoundary, primaryBreak, secondaryBreak: INT ← 0;
insidePhonemes, primInsidePhonemes, secInsidePhonemes: BOOL ← FALSE;
prevc: CHAR ← IO.SP; -- i-2
c: CHAR ← text.Fetch[0]; -- i-1
FOR i:
INT
IN [1..maxlen]
DO
nextc: CHAR ← ProtectFetch[text, i]; -- i
SELECT c
FROM
IO.
ESC => {
SELECT nextc
FROM
'P => {
-- c is the first character of a command; may be a phoneme command
cmdBoundary ← i-1;
IF PhonemeCmd[text, i+1] THEN insidePhonemes ← TRUE;
};
'\\ => {
-- nextc is the end of a DECtalk command
cmdBoundary ← i+1;
insidePhonemes ← FALSE;
};
'[, 'c, IO.SP => c may be the first character of some other DECtalk command, but the user is not permitted to send these commands.
ENDCASE; -- c is some other ESC (what is this silly user doing??)
};
IO.
SP,
IO.
TAB,
IO.
CR,
IO.
LF => {
secondaryBreak ← i;
secInsidePhonemes ← insidePhonemes;
SELECT prevc
FROM
'. => IF NOT SpecialAbbrev[text, i-4] THEN {primaryBreak ← i;
primInsidePhonemes ← insidePhonemes};
', , ';, '!, '?, '), '}, '], '", '' => {primaryBreak ← i;
primInsidePhonemes ← insidePhonemes};
ENDCASE => {
SELECT nextc
FROM
'(, '{, '[, '", '', '` => {primaryBreak ← i; -- the last one is an open quote
primInsidePhonemes ← insidePhonemes};
ENDCASE;
};
};
ENDCASE;
prevc ← c; c ← nextc;
ENDLOOP;
Now choose an appropriate spot to break the text....
IF primaryBreak > 0
THEN
RETURN [primaryBreak, primInsidePhonemes] -- could be inside a dict cmd, oh well
ELSE
IF cmdBoundary > 0
THEN
RETURN [cmdBoundary, FALSE]
ELSE
IF secondaryBreak > 0
THEN
RETURN [secondaryBreak, secInsidePhonemes]
ELSE
RETURN [0, insidePhonemes] -- this is a wierd string: no good place to break
PhonemeCmd:
PROC [text: Rope.
ROPE, i:
INT]
RETURNS [
BOOL ←
FALSE] = {
A DECtalk phoneme command looks like ESC P 0 ; 0 z phonemic text ESC \
The zeros are optional, but we only recognize the cases of both zeros there or both zeros missing.
nextw: Rope.ROPE ← ProtectSubstr[text: text, start: i, len: 2];
IF Rope.Equal[s1: nextw, s2: ";z", case: TRUE] THEN RETURN[TRUE];
nextw ← ProtectSubstr[text: text, start: i, len: 4];
IF Rope.Equal[s1: nextw, s2: "0;0z", case: TRUE] THEN RETURN[TRUE];
};
SpecialAbbrev:
PROC [text: Rope.
ROPE, i:
INT]
RETURNS [
BOOL ←
FALSE] = {
DECtalk: St. followed by a capital letter = Saint; not followed by a capital letter = Street. Dr. followed by a capital letter = Doctor; not followed by a capital letter = Drive.
Prose adds: Ft. followed by a capital letter = Fort; not followed by a capital letter = feet. (The DECtalk distinguishes between these latter two on the basis of capitalization: Ft. = Fort, ft. = feet. But what about FT.? Seems like a bug!)
prevw: Rope.ROPE ← ProtectSubstr[text: text, start: i, len: 2];
prevc:
CHAR ← ProtectFetch[text, i-1];
If we run off the beginning of the string, there must have been a legal break there.
SELECT prevc
FROM
IO.SP, IO.TAB, IO.CR, IO.LF => NULL;
ENDCASE => RETURN[FALSE];
IF Rope.Equal[s1: prevw, s2: "ft", case:
FALSE]
OR Rope.Equal[s1: prevw, s2: "st", case:
FALSE]
OR Rope.Equal[s1: prevw, s2: "dr", case:
FALSE]
THEN
FOR j:
INT ← i+1, j+1
DO
scanc: CHAR ← ProtectFetch[text: text, index: j, default: 'a];
SELECT scanc
FROM
IN ['A..'Z] => RETURN[TRUE];
IN ['a..'z] => RETURN[FALSE];
ENDCASE;
ENDLOOP;
};
ProtectFetch:
PROC [text: Rope.
ROPE, index:
INT, default:
CHAR ← IO.
SP]
RETURNS [char:
CHAR] ~ {
ENABLE RuntimeError.BoundsFault => {char ← default; CONTINUE};
char ← text.Fetch[index];
};
ProtectSubstr:
PROC [text: Rope.
ROPE, start:
INT, len:
INT]
RETURNS [substr: Rope.
ROPE ← ""] ~ {
ENABLE RuntimeError.BoundsFault => CONTINUE;
substr ← text.Substr[start, len];
};
}.
Polle Zellweger (PTZ) July 17, 1986 6:02:46 pm PDT
changes to: SynthesizerImpl, BreakText, FindTextBreak
Polle Zellweger (PTZ) July 18, 1986 11:41:44 am PDT
changes to: DIRECTORY, BreakText, FindTextBreak, PhonemeCmd, nextc (local of FindTextBreak), nextw (local of PhonemeCmd), prevw (local of SpecialAbbrev), SpecialAbbrev, scanc (local of SpecialAbbrev), SynthesizerImpl
Polle Zellweger (PTZ) July 18, 1986 5:17:06 pm PDT
changes to: BreakText, FindTextBreak, PhonemeCmd, prevw (local of SpecialAbbrev), prevc (local of SpecialAbbrev), SpecialAbbrev, nextw (local of PhonemeCmd)
Polle Zellweger (PTZ) July 28, 1986 8:57:29 pm PDT
changes to: nextc (local of FindTextBreak), nextw (local of PhonemeCmd), PhonemeCmd, prevw (local of SpecialAbbrev), prevc (local of SpecialAbbrev), scanc (local of SpecialAbbrev)
Polle Zellweger (PTZ) July 28, 1986 9:30:23 pm PDT
changes to: FindTextBreak, PhonemeCmd, SpecialAbbrev, ProtectFetch, ProtectSubstr
Polle Zellweger (PTZ) July 28, 1986 10:34:46 pm PDT
changes to: FindTextBreak