Add files by Jack
diff --git a/Mac/Demo/speech/grail.py b/Mac/Demo/speech/grail.py
new file mode 100644
index 0000000..d926569
--- /dev/null
+++ b/Mac/Demo/speech/grail.py
@@ -0,0 +1,228 @@
+# Pass this program the Holy Grail script on stdin.
+import sys
+import string
+import stdwin
+from stdwinevents import *
+
+try:
+	import macspeech
+except ImportError:
+	macspeech = None
+
+WINWIDTH = 1000
+scrw, scrh = stdwin.getscrsize()
+if WINWIDTH > 0.8*scrw:
+	WINWIDTH = int(0.8*scrw)
+BLACK = stdwin.fetchcolor('black')
+RED = stdwin.fetchcolor('red')
+BLUE = stdwin.fetchcolor('blue')
+
+done='done'
+
+class MacSpeaker:
+	def __init__(self):
+		self.voices = []
+		self.nvoices = macspeech.CountVoices()
+		self.curvoice = 1
+		self.rate = 1.0
+		
+	def _newvoice(self):
+		vd = macspeech.GetIndVoice(self.curvoice)
+		sc = vd.NewChannel()
+		self.curvoice = self.curvoice + 1
+		if self.curvoice > self.nvoices:
+			self.curvoice = 1
+		return sc
+		
+	def newvoices(self, n):
+		self.voices = []
+		for i in range(n):
+			self.voices.append(self._newvoice())
+		if self.rate <> 1.0:
+			self.setrate(1.0)
+			
+	def setrate(self, factor):
+		self.rate = self.rate*factor
+		for v in self.voices:
+			r = v.GetRate()
+			v.SetRate(r*factor)
+					
+	def speak(self, i, text):
+		self.voices[i-1].SpeakText(text)
+		
+	def busy(self):
+		return macspeech.Busy()
+
+[NOTHING, NEWSCENE, ACT, TEXT, MORETEXT] = range(5)
+def parseline(line):
+	stripline = string.strip(line)
+	if not stripline:
+		return NOTHING, ''
+	if stripline[:5] == 'Scene':
+		return NEWSCENE, stripline
+	if line[0] == '[':
+		return ACT, stripline
+	if line[0] == ' ' and ':' in line:
+		splitline = string.splitfields(stripline, ':')
+		stripline = string.joinfields(splitline[1:], ':')
+		return TEXT, (splitline[0], string.strip(stripline))
+	return MORETEXT, stripline
+
+def readscript(file):
+	lines = file.readlines()
+	acts = []
+	actor_dict = {}
+	longest = 0
+	prev_act = 0
+	for i in range(len(lines)):
+		tp, data = parseline(lines[i])
+		if tp == NEWSCENE:
+			acts.append(actor_dict.keys(), lines[prev_act:i])
+			prev_act = i
+			actor_dict = {}
+		elif tp == TEXT:
+			actor_dict[data[0]] = 1
+		lines[i] = tp, data
+	return acts[1:]
+
+class Main:
+	def __init__(self):
+		if macspeech:
+			self.speaker = MacSpeaker()
+		else:
+			self.speaker = None
+		sys.stdin = open('SCRIPT', 'r')
+		self.acts = readscript(sys.stdin)
+		maxactor = 0
+		for actorlist, actdata in self.acts:
+			if len(actorlist) > maxactor:
+				maxactor = len(actorlist)
+		if not self.loadnextact():
+			print 'No acts to play!'
+			raise done
+		self.lh = stdwin.lineheight()
+		self.winheight = (maxactor+2)*self.lh
+		stdwin.setdefwinsize(WINWIDTH, self.winheight)
+		self.win = stdwin.open('The Play')
+		self.win.setdocsize(WINWIDTH, self.winheight)
+		self.win.change(((0,0),(WINWIDTH, self.winheight)))
+		self.menu = self.win.menucreate('Play')
+		self.menu.additem('Faster', '+')
+		self.menu.additem('Slower', '-')
+		self.menu.additem('Quit', 'Q')
+		self.speed = 4
+		
+	def done(self):
+		del self.win
+		del self.menu
+
+	def loadnextact(self):
+		if not self.acts: return 0
+		actors, lines = self.acts[0]
+		del self.acts[0]
+		prevactor = 0
+		for i in range(len(lines)):
+			tp, data = lines[i]
+			if tp == NOTHING:
+				continue
+			elif tp in (NEWSCENE, ACT):
+				lines[i] = 0, data
+			elif tp == TEXT:
+				prevactor = actors.index(data[0])
+				lines[i] = prevactor+1, data[1]
+			else:
+				lines[i] = prevactor+1, data
+		self.lines = lines
+		self.actors = [''] + actors
+		self.actorlines = [''] * len(self.actors)
+		if self.speaker:
+			self.speaker.newvoices(len(self.actors)-1)
+		self.prevline = 0
+		self.actwidth = 0
+		for a in self.actors:
+			w = stdwin.textwidth(a)
+			if w > self.actwidth:
+				self.actwidth = w
+		return 1
+
+	def loadnextline(self):
+		if not self.lines: return 0
+		self.actorlines[self.prevline] = ''
+		top = self.lh*self.prevline
+		self.win.change(((0, top), (WINWIDTH, top+self.lh)))
+		line, data = self.lines[0]
+		del self.lines[0]
+		self.actorlines[line] = data
+		self.prevline = line
+		top = self.lh*self.prevline
+		self.win.change(((0, top), (WINWIDTH, top+self.lh)))
+		if line == 0:
+			self.win.settimer(5*self.speed)
+		else:
+			if self.speaker:
+				self.speaker.speak(line, data)
+				tv = 1
+			else:
+				nwords = len(string.split(data))
+				tv = self.speed*(nwords+1)
+			self.win.settimer(tv)
+		return 1
+
+	def timerevent(self):
+		if self.speaker and self.speaker.busy():
+			self.win.settimer(1)
+			return
+		while 1:
+			if self.loadnextline(): return
+			if not self.loadnextact():
+				stdwin.message('The END')
+				self.win.close()
+				raise done
+			self.win.change(((0,0), (WINWIDTH, self.winheight)))
+
+	def redraw(self, top, bottom, draw):
+		for i in range(len(self.actors)):
+			tpos = i*self.lh
+			bpos = (i+1)*self.lh-1
+			if tpos < bottom and bpos > top:
+				draw.setfgcolor(BLUE)
+				draw.text((0, tpos), self.actors[i])
+				if i == 0:
+					draw.setfgcolor(RED)
+				else:
+					draw.setfgcolor(BLACK)
+				draw.text((self.actwidth+5, tpos), self.actorlines[i])
+
+	def run(self):
+		self.win.settimer(10)
+		while 1:
+			ev, win, arg = stdwin.getevent()
+			if ev == WE_DRAW:
+				((left, top), (right, bot)) = arg
+				self.redraw(top, bot, self.win.begindrawing())
+			elif ev == WE_TIMER:
+				self.timerevent()
+			elif ev == WE_CLOSE:
+				self.win.close()
+				raise done
+			elif ev == WE_MENU and arg[0] == self.menu:
+				if arg[1] == 0:
+					if self.speed > 1:
+						self.speed = self.speed/2
+						if self.speaker:
+							self.speaker.setrate(1.4)
+				elif arg[1] == 1:
+					self.speed = self.speed * 2
+					if self.speaker:
+						self.speaker.setrate(0.7)
+				elif arg[1] == 2:
+					self.win.close()
+					raise done
+
+if 1:
+	main = Main()
+	try:
+		main.run()
+	except done:
+		pass
+	del main