#get the full application at http://sayzme.sourceforge.net
from Tkinter import *
import win32com.client
import pythoncom
from win32com.client import *
import time
from random import *
defaultNamedOptArg=pythoncom.Missing
defaultNamedNotOptArg=pythoncom.Missing
defaultUnnamedArg=pythoncom.Missing
gencache.EnsureModule('{EEE78583-FE22-11D0-8BEF-0060081841DE}', 0, 1, 0)
class Sapi4:
voice_list = []
def __init__(self):
#init
self.SpeedPercent=StringVar()
self.PitchPercent=StringVar()
self.VolumeLeftPercent=StringVar()
self.VolumeRightPercent=StringVar()
try:
self.directss = win32com.client.DispatchWithEvents("{EEE78591-FE22-11D0-8BEF-0060081841DE}", Sapi4Events)
voice_no = self.directss.Find(0)
print 'Woohoo: have found engine'
self.voice = self.directss.Select(voice_no)
voice_name = self.directss.ModeName(voice_no)
self.sapi_compliant = 1
if len(self.voice_list) < 1:
self.getVoiceList()
self.selectVoice(voice_name)
except:
print 'Bugger: unable to initialise speech!'
#self.quit()
return None
def selectVoice(self, voice_name=''):
#select voice
voice_index = 0
voice_no = 0
found = 0
for voice_compare in self.voice_list:
voice_no = voice_no + 1
if voice_compare == voice_name:
found = 1
break
if found == 0:
voice_no = 1
self.directss.AudioReset()
try:
self.directss.Select(voice_no)
self.sapi_compliant = 1
except:
self.sapi_compliant = 0
self.directss.Speak(' ') #don't understand why we need this here but voice doesn't change otherwise
self.voice_no = voice_no
self.voice_index = voice_no - 1
self.voice_name = self.directss.ModeName(voice_no)
#print 'Selected voice: ' + self.voice_name
self.MinSpeed = self.directss.MinSpeed
self.MaxSpeed = self.directss.MaxSpeed
self.Speed = self.directss.Speed
#print 'Speed: from ' + str(self.MinSpeed) + ' to ' + str(self.MaxSpeed) + ' - current ' + str(self.Speed)
self.MinPitch = self.directss.MinPitch
self.MaxPitch = self.directss.MaxPitch
self.Pitch = self.directss.Pitch
#print 'Pitch: from ' + str(self.MinPitch) + ' to ' + str(self.MaxPitch) + ' - current ' + str(self.Pitch)
self.MinVolumeLeft = self.directss.MinVolumeLeft
self.MaxVolumeLeft = self.directss.MaxVolumeLeft
self.VolumeLeft = self.directss.VolumeLeft
#print 'VolumeLeft: from ' + str(self.MinVolumeLeft) + ' to ' + str(self.MaxVolumeLeft) + ' - current ' + str(self.VolumeLeft)
self.MinVolumeRight = self.directss.MinVolumeRight
self.MaxVolumeRight = self.directss.MaxVolumeRight
self.VolumeRight = self.directss.VolumeRight
#print 'VolumeRight: from ' + str(self.MinVolumeRight) + ' to ' + str(self.MaxVolumeRight) + ' - current ' + str(self.VolumeRight)
#print
return voice_no
def getVoiceList(self):
#fill list of avaliable voices
voice_count = self.directss.CountEngines
voice_no = 1
self.voice_list=[]
while voice_no < voice_count + 1:
voice_name = self.directss.ModeName(voice_no)
#print 'adding voice' + voice_name
self.voice_list.append(voice_name)
voice_no = voice_no + 1
return None
def setSpeed(self, speed):
#set voice speed
self.directss.Speed = int(speed)
speed_percent = str(((self.directss.Speed - self.directss.MinSpeed)*100) / (self.directss.MaxSpeed-self.directss.MinSpeed)) + '%'
self.SpeedPercent.set(speed_percent)
return None
def setPitch(self, pitch):
#set voice pitch
self.directss.Pitch = int(pitch)
pitch_percent = str(((self.directss.Pitch - self.directss.MinPitch)*100) / (self.directss.MaxPitch-self.directss.MinPitch)) + '%'
self.PitchPercent.set(pitch_percent)
return None
def setVolumeLeft(self, volume):
#set voice volume left
self.directss.VolumeLeft = int(volume)
volume_percent = str(((self.directss.VolumeLeft - self.directss.MinVolumeLeft)*100) / \
(self.directss.MaxVolumeLeft-self.directss.MinVolumeLeft)) + '%'
self.VolumeLeftPercent.set(volume_percent)
self.setVolumeRight(volume) #change right volume to match left
return None
def setVolumeRight(self, volume):
#set voice volume right - this seems to have no effect - left volume appears to
#control both left and right channels for the engines tested with
self.directss.VolumeRight = int(volume)
volume_percent = str(((self.directss.VolumeRight - self.directss.MinVolumeRight)*100) / \
(self.directss.MaxVolumeRight-self.directss.MinVolumeRight)) + '%'
self.VolumeRightPercent.set(volume_percent)
return None
class Sapi4Events:
def OnAudioStart(self, hi=defaultNamedNotOptArg, lo=defaultNamedNotOptArg):
# method AudioStart
global gwordpositionjumpto
global gspeaking
global gwords
global gwordposition
global gcharposition
global ghide
global gpaused
gspeaking = int(1)
if gpaused == 0:
gwordposition = int(0)
gcharposition = int(0)
ghide = 1
if gwordpositionjumpto > 0:
wordposition = 0
while wordposition < (gwordpositionjumpto -1):
gcharposition = gcharposition + len(gwords[wordposition]) + 1
wordposition = wordposition + 1
gwordposition = gwordpositionjumpto
gwordpositionjumpto = 0
def OnWordPosition(self, hi=defaultNamedNotOptArg, lo=defaultNamedNotOptArg, byteoffset=defaultNamedNotOptArg):
# method WordPosition
global gwords
global gwordposition
global gcharposition
gwordposition = gwordposition + 1
word = gwords[gwordposition-1]
#print word + ' ' + str(gwordposition)
gcharposition = gcharposition + len(word) + 1
def OnAudioStop(self, hi=defaultNamedNotOptArg, lo=defaultNamedNotOptArg):
#method AudioStop
global gspeaking
gspeaking = int(0)
class ExampleSapi:
def __init__(self, dialog):
self.dialog = dialog
dialog.title("Voice Settings")
self.sapi = Sapi4()
self.frame_dialog = Frame(dialog, height=500)
self.frame_dialog.pack(side=TOP, expand=NO, fill=BOTH)
#buttons
frame_button = Frame(dialog)
frame_button.pack(side=BOTTOM, expand=NO, fill=X)
button = Button(frame_button, text='Close', command=dialog.destroy, width=10, foreground='DarkRed')
button.pack(side=RIGHT, padx=2, pady=2)
button = Button(frame_button, text='Test', command=self.test, width=10, foreground='DarkBlue')
button.pack(side=RIGHT, padx=2, pady=2)
frame_left = Frame(self.frame_dialog)
frame_left.pack(side=TOP, expand=YES, fill=BOTH)
scrollbar = Scrollbar(frame_left, orient=VERTICAL)
self.listbox = Listbox(frame_left, yscrollcommand=scrollbar.set, exportselection=0, font="Arial 10")
scrollbar.config(command=self.listbox.yview)
scrollbar.pack(side=RIGHT, fill=Y)
for item in self.sapi.voice_list:
self.listbox.insert(END, item)
self.listbox.pack(side=LEFT, padx=2, pady=2, expand=YES, fill=BOTH)
self.listbox.select_set(self.sapi.voice_index)
self.listbox.see(self.sapi.voice_index)
#settings frame
frame_right = Frame(self.frame_dialog)
frame_right.pack(side=TOP, expand=NO, fill=BOTH)
#speed
self.label_speed = Label(frame_right, text=" Speed:", font="Arial 10")
self.label_speed.grid(row=0, col=0, sticky=E, padx=2, pady=1)
self.scale_speed = Scale(frame_right, length=300, orient='horizontal', command=self.sapi.setSpeed, showvalue=0)
self.scale_speed.grid(row=0, column=1, sticky=W, padx=2, pady=1)
self.label_speed_percent = Label(frame_right, textvariable=self.sapi.SpeedPercent, width=5)
self.label_speed_percent.grid(row=0, column=2, sticky=W, padx=2, pady=1)
#pitch
self.label_pitch = Label(frame_right, text=" Pitch:", font="Arial 10")
self.label_pitch.grid(row=1, column=0, sticky=E, padx=2, pady=1)
self.scale_pitch = Scale(frame_right, length=300, orient='horizontal', command=self.sapi.setPitch, showvalue=0)
self.scale_pitch.grid(row=1, column=1, sticky=W, padx=2, pady=1)
self.label_pitch_percent = Label(frame_right, textvariable=self.sapi.PitchPercent, width=5)
self.label_pitch_percent.grid(row=1, column=2, sticky=W, padx=2, pady=1)
#volume left
self.label_volume_left = Label(frame_right, text=" Volume:", font="Arial 10")
self.label_volume_left.grid(row=2, column=0, sticky=E, padx=2, pady=1)
self.scale_volume_left = Scale(frame_right, length=300, orient='horizontal', command=self.sapi.setVolumeLeft, showvalue=0)
self.scale_volume_left.grid(row=2, column=1, sticky=W, padx=2, pady=1)
self.label_volume_left_percent = Label(frame_right, textvariable=self.sapi.VolumeLeftPercent, width=5)
self.label_volume_left_percent.grid(row=2, column=2, sticky=W, padx=2, pady=1)
#volume right - does not appear to work
# label = Label(frame_right, text=" Volume Right:", font="Arial 10")
# label.grid(row=3, col=0, sticky=E, padx=2, pady=1)
# self.scale_volume_right = Scale(frame_right, length=200, orient='horizontal', command=self.sapi.setVolumeRight, showvalue=0)
# self.scale_volume_right.grid(row=3, column=1, sticky=W, padx=2, pady=1)
# label = Label(frame_right, textvariable=self.sapi.VolumeRightPercent, width=5)
# label.grid(row=3, column=2, sticky=W, padx=2, pady=1)
#test text
self.test_text = StringVar()
self.test_text.set('This is a test. This is a test. This is a test.')
label = Label(frame_right, text=" Test Text:", font="Arial 10")
label.grid(row=3, column=0, sticky=E, padx=2, pady=1)
text = Entry(frame_right, textvariable = self.test_text, font="Arial 10", width=50, exportselection=0)
text.grid(row=3, column=1, columnspan=2, sticky=W, padx=2, pady=1)
#canvas
self.canvas = Canvas(self.frame_dialog, height=120)
self.canvas.pack(expand=NO, fill=BOTH)
self.drawFace()
self.canvas.after(1000, self.drawFaceBlink)
self.listbox.after(500, self.poll)
self.setVoice(self.listbox.get(self.listbox.curselection()[0]))
self.centerMe()
return None
def setVoice(self, voice_name):
#select voice
voice_no = self.sapi.selectVoice(voice_name)
self.scale_speed.configure(from_=self.sapi.directss.MinSpeed, to=self.sapi.directss.MaxSpeed)
self.scale_pitch.configure(from_=self.sapi.directss.MinPitch, to=self.sapi.directss.MaxPitch)
self.scale_volume_left.configure(from_=self.sapi.directss.MinVolumeLeft, to=self.sapi.directss.MaxVolumeLeft)
#self.scale_volume_right.configure(from_=self.sapi.directss.MinVolumeRight, to=self.sapi.directss.MaxVolumeRight)
#self.scale_volume_right.set(self.sapi.VolumeRight)
#viavoice8 min max volumes are back to front - so hide volume scale
#viavoice8 also change thier min speed and pitch values to the current values - so hide them too i guess
self.scale_speed.set(self.sapi.directss.Speed)
self.scale_pitch.set(self.sapi.directss.Pitch)
self.scale_volume_left.set(self.sapi.directss.VolumeLeft)
if (self.sapi.directss.MinVolumeLeft > self.sapi.directss.MaxVolumeLeft) or self.sapi.sapi_compliant==0:
self.label_speed.grid_forget()
self.scale_speed.grid_forget()
self.label_speed_percent.grid_forget()
self.label_pitch.grid_forget()
self.scale_pitch.grid_forget()
self.label_pitch_percent.grid_forget()
self.label_volume_left.grid_forget()
self.scale_volume_left.grid_forget()
self.label_volume_left_percent.grid_forget()
self.canvas.forget()
else:
self.label_speed.grid(row=0, col=0, sticky=E, padx=2, pady=1)
self.scale_speed.grid(row=0, column=1, sticky=W, padx=2, pady=1)
self.label_speed_percent.grid(row=0, column=2, sticky=W, padx=2, pady=1)
self.label_pitch.grid(row=1, col=0, sticky=E, padx=2, pady=1)
self.scale_pitch.grid(row=1, column=1, sticky=W, padx=2, pady=1)
self.label_pitch_percent.grid(row=1, column=2, sticky=W, padx=2, pady=1)
self.label_volume_left.grid(row=2, col=0, sticky=E, padx=2, pady=1)
self.scale_volume_left.grid(row=2, column=1, sticky=W, padx=2, pady=1)
self.label_volume_left_percent.grid(row=2, column=2, sticky=W, padx=2, pady=1)
self.canvas.pack()
speak_text = 'You have selected the voice of ' + voice_name + '.'
self.sapi.directss.Speak(speak_text)
return None
def poll(self):
new_voice_name = self.listbox.get(self.listbox.curselection()[0])
if new_voice_name != self.sapi.voice_name:
self.setVoice(new_voice_name)
self.drawFaceMouthTalk()
self.listbox.after(90, self.poll)
return None
def test(self):
#test
self.sapi.directss.AudioReset()
if len(self.test_text.get()) < 1:
self.test_text = 'This is a test. This is a test. This is a test.'
self.sapi.directss.Speak(self.test_text.get())
return None
def drawFace(self):
#draw face
self.face = self.canvas.create_oval(140, 10, 240, 110, tag='face', fill='yellow', width=3)
self.eyeL = self.canvas.create_oval(170, 40, 180, 60, tag='eye', fill='black')
self.eyeR = self.canvas.create_oval(200, 40, 210, 60, tag='eye', fill='black')
self.mouthClosed = self.canvas.create_polygon(150, 60, 190, 100, 230, 60, 190, 100,
smooth=1, width=3, outline='Black', fill='Red')
#hidden at startup
self.mouthOpen1 = self.canvas.create_polygon(150, 60, 170, 75, 190, 90, 210, 75, 230, 60, 190, 90,
splinesteps=20, smooth=1, width=3, outline='Black', fill='Red')
self.mouthOpen2 = self.canvas.create_polygon(150, 60, 160, 75, 190, 95, 220, 75, 230, 60, 190, 90,
splinesteps=20, smooth=1, width=3, outline='Black', fill='Red')
self.mouthOpen3 = self.canvas.create_polygon(150, 60, 170, 75, 190, 95, 210, 75, 230, 60, 190, 90,
splinesteps=20, smooth=1, width=3, outline='Black', fill='Red')
self.mouthOpen4 = self.canvas.create_polygon(150, 60, 160, 80, 190, 100, 220, 80, 230, 60, 190, 90,
splinesteps=20, smooth=1, width=3, outline='Black', fill='Red')
self.canvas.lower(self.mouthOpen1)
self.canvas.lower(self.mouthOpen2)
self.canvas.lower(self.mouthOpen3)
self.canvas.lower(self.mouthOpen4)
self.blinkL = self.canvas.create_oval(165, 49, 185, 51, tag='eye', fill='black')
self.blinkR = self.canvas.create_oval(195, 49, 215, 51, tag='eye', fill='black')
self.canvas.lower(self.blinkL)
self.canvas.lower(self.blinkR)
return None
def drawFaceMouthTalk(self):
#open mouth
global gspeaking
if gspeaking == 0:
self.canvas.lift(self.mouthClosed)
self.canvas.lower(self.mouthOpen1)
self.canvas.lower(self.mouthOpen2)
self.canvas.lower(self.mouthOpen3)
self.canvas.lower(self.mouthOpen4)
return None
mouthdisplay = randrange(1,5)
self.canvas.lower(self.mouthClosed)
self.canvas.lower(self.mouthOpen1)
self.canvas.lower(self.mouthOpen2)
self.canvas.lower(self.mouthOpen3)
self.canvas.lower(self.mouthOpen4)
if mouthdisplay == 1:
self.canvas.lift(self.mouthOpen1)
if mouthdisplay == 2:
self.canvas.lift(self.mouthOpen2)
if mouthdisplay == 3:
self.canvas.lift(self.mouthOpen3)
if mouthdisplay == 4:
self.canvas.lift(self.mouthOpen4)
return None
def drawFaceBlink(self):
#blink
self.canvas.lift(self.blinkL)
self.canvas.lift(self.blinkR)
self.canvas.lower(self.eyeL)
self.canvas.lower(self.eyeR)
self.canvas.after(200, self.drawFaceUnblink)
return None
def drawFaceUnblink(self):
self.canvas.lift(self.eyeL)
self.canvas.lift(self.eyeR)
self.canvas.lower(self.blinkL)
self.canvas.lower(self.blinkR)
blinkat = randrange(1,10) * 1000
self.canvas.after(blinkat, self.drawFaceBlink)
return None
def centerMe(self):
#center
top = self.dialog
top.update()
sw = top.winfo_screenwidth()
sh = top.winfo_screenheight()
w = top.winfo_width()
h = 470#top.winfo_height()
x = (sw - w)/2
y = (sh - h)/2
geom = '%dx%d+%d+%d' % (w, h, x, y)
top.geometry(geom)
return None
if __name__ == "__main__":
root = Tk()
Test = ExampleSapi(root)
root.mainloop()