Added ref to Misc/NEWS file; added hint to run regen script on Linux.
[python/dscho.git] / Mac / Demo / speech / grail.py
blobd9265698448277e116664dd19ad23e45f028211b
1 # Pass this program the Holy Grail script on stdin.
2 import sys
3 import string
4 import stdwin
5 from stdwinevents import *
7 try:
8 import macspeech
9 except ImportError:
10 macspeech = None
12 WINWIDTH = 1000
13 scrw, scrh = stdwin.getscrsize()
14 if WINWIDTH > 0.8*scrw:
15 WINWIDTH = int(0.8*scrw)
16 BLACK = stdwin.fetchcolor('black')
17 RED = stdwin.fetchcolor('red')
18 BLUE = stdwin.fetchcolor('blue')
20 done='done'
22 class MacSpeaker:
23 def __init__(self):
24 self.voices = []
25 self.nvoices = macspeech.CountVoices()
26 self.curvoice = 1
27 self.rate = 1.0
29 def _newvoice(self):
30 vd = macspeech.GetIndVoice(self.curvoice)
31 sc = vd.NewChannel()
32 self.curvoice = self.curvoice + 1
33 if self.curvoice > self.nvoices:
34 self.curvoice = 1
35 return sc
37 def newvoices(self, n):
38 self.voices = []
39 for i in range(n):
40 self.voices.append(self._newvoice())
41 if self.rate <> 1.0:
42 self.setrate(1.0)
44 def setrate(self, factor):
45 self.rate = self.rate*factor
46 for v in self.voices:
47 r = v.GetRate()
48 v.SetRate(r*factor)
50 def speak(self, i, text):
51 self.voices[i-1].SpeakText(text)
53 def busy(self):
54 return macspeech.Busy()
56 [NOTHING, NEWSCENE, ACT, TEXT, MORETEXT] = range(5)
57 def parseline(line):
58 stripline = string.strip(line)
59 if not stripline:
60 return NOTHING, ''
61 if stripline[:5] == 'Scene':
62 return NEWSCENE, stripline
63 if line[0] == '[':
64 return ACT, stripline
65 if line[0] == ' ' and ':' in line:
66 splitline = string.splitfields(stripline, ':')
67 stripline = string.joinfields(splitline[1:], ':')
68 return TEXT, (splitline[0], string.strip(stripline))
69 return MORETEXT, stripline
71 def readscript(file):
72 lines = file.readlines()
73 acts = []
74 actor_dict = {}
75 longest = 0
76 prev_act = 0
77 for i in range(len(lines)):
78 tp, data = parseline(lines[i])
79 if tp == NEWSCENE:
80 acts.append(actor_dict.keys(), lines[prev_act:i])
81 prev_act = i
82 actor_dict = {}
83 elif tp == TEXT:
84 actor_dict[data[0]] = 1
85 lines[i] = tp, data
86 return acts[1:]
88 class Main:
89 def __init__(self):
90 if macspeech:
91 self.speaker = MacSpeaker()
92 else:
93 self.speaker = None
94 sys.stdin = open('SCRIPT', 'r')
95 self.acts = readscript(sys.stdin)
96 maxactor = 0
97 for actorlist, actdata in self.acts:
98 if len(actorlist) > maxactor:
99 maxactor = len(actorlist)
100 if not self.loadnextact():
101 print 'No acts to play!'
102 raise done
103 self.lh = stdwin.lineheight()
104 self.winheight = (maxactor+2)*self.lh
105 stdwin.setdefwinsize(WINWIDTH, self.winheight)
106 self.win = stdwin.open('The Play')
107 self.win.setdocsize(WINWIDTH, self.winheight)
108 self.win.change(((0,0),(WINWIDTH, self.winheight)))
109 self.menu = self.win.menucreate('Play')
110 self.menu.additem('Faster', '+')
111 self.menu.additem('Slower', '-')
112 self.menu.additem('Quit', 'Q')
113 self.speed = 4
115 def done(self):
116 del self.win
117 del self.menu
119 def loadnextact(self):
120 if not self.acts: return 0
121 actors, lines = self.acts[0]
122 del self.acts[0]
123 prevactor = 0
124 for i in range(len(lines)):
125 tp, data = lines[i]
126 if tp == NOTHING:
127 continue
128 elif tp in (NEWSCENE, ACT):
129 lines[i] = 0, data
130 elif tp == TEXT:
131 prevactor = actors.index(data[0])
132 lines[i] = prevactor+1, data[1]
133 else:
134 lines[i] = prevactor+1, data
135 self.lines = lines
136 self.actors = [''] + actors
137 self.actorlines = [''] * len(self.actors)
138 if self.speaker:
139 self.speaker.newvoices(len(self.actors)-1)
140 self.prevline = 0
141 self.actwidth = 0
142 for a in self.actors:
143 w = stdwin.textwidth(a)
144 if w > self.actwidth:
145 self.actwidth = w
146 return 1
148 def loadnextline(self):
149 if not self.lines: return 0
150 self.actorlines[self.prevline] = ''
151 top = self.lh*self.prevline
152 self.win.change(((0, top), (WINWIDTH, top+self.lh)))
153 line, data = self.lines[0]
154 del self.lines[0]
155 self.actorlines[line] = data
156 self.prevline = line
157 top = self.lh*self.prevline
158 self.win.change(((0, top), (WINWIDTH, top+self.lh)))
159 if line == 0:
160 self.win.settimer(5*self.speed)
161 else:
162 if self.speaker:
163 self.speaker.speak(line, data)
164 tv = 1
165 else:
166 nwords = len(string.split(data))
167 tv = self.speed*(nwords+1)
168 self.win.settimer(tv)
169 return 1
171 def timerevent(self):
172 if self.speaker and self.speaker.busy():
173 self.win.settimer(1)
174 return
175 while 1:
176 if self.loadnextline(): return
177 if not self.loadnextact():
178 stdwin.message('The END')
179 self.win.close()
180 raise done
181 self.win.change(((0,0), (WINWIDTH, self.winheight)))
183 def redraw(self, top, bottom, draw):
184 for i in range(len(self.actors)):
185 tpos = i*self.lh
186 bpos = (i+1)*self.lh-1
187 if tpos < bottom and bpos > top:
188 draw.setfgcolor(BLUE)
189 draw.text((0, tpos), self.actors[i])
190 if i == 0:
191 draw.setfgcolor(RED)
192 else:
193 draw.setfgcolor(BLACK)
194 draw.text((self.actwidth+5, tpos), self.actorlines[i])
196 def run(self):
197 self.win.settimer(10)
198 while 1:
199 ev, win, arg = stdwin.getevent()
200 if ev == WE_DRAW:
201 ((left, top), (right, bot)) = arg
202 self.redraw(top, bot, self.win.begindrawing())
203 elif ev == WE_TIMER:
204 self.timerevent()
205 elif ev == WE_CLOSE:
206 self.win.close()
207 raise done
208 elif ev == WE_MENU and arg[0] == self.menu:
209 if arg[1] == 0:
210 if self.speed > 1:
211 self.speed = self.speed/2
212 if self.speaker:
213 self.speaker.setrate(1.4)
214 elif arg[1] == 1:
215 self.speed = self.speed * 2
216 if self.speaker:
217 self.speaker.setrate(0.7)
218 elif arg[1] == 2:
219 self.win.close()
220 raise done
222 if 1:
223 main = Main()
224 try:
225 main.run()
226 except done:
227 pass
228 del main