#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()