[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[pygame] Voice Text To Speech and Wav File Make and Save



Hi!

    Does anyone have a good example on capturing key events?

    I got my text to speech to work better, will in the future remove the
buttons and only have the file menu.
    It seems to work fine and now use sub menu's for voice settings.

    What will be needed as an event to capture the cursor movement inside
the text box. How do I get the key and cursor events so I can speak when
doing ctrl cursor or up/down cursor events to say lines, words, and letters?
    I have tried getting the key event but gets ignored, so I need the
proper kind and location to catch the text box key events. I erased my test,
but below and attached are the complete setup.

        Bruce



desc="""Using two nested sizers, the main one with vertical layout
and the embedded one with horizontal layout
For setting voice parms:"""
#Editor.py
import wx
import os
import Sapi5
tts = Sapi5.Create( {"name":"Mary"})
purge = tts._purge
async = tts._async
punc = tts._punc
MN=0
ID=1
HK=2
KD=3
MF=4
MF2=5
DW_ID=1000
class MainWindow(wx.Frame):
    def __init__(self, parent, id, title):
        self.dirname=os.getcwd()  #SEARCH FROM PRESENT DIRECTORY!
        self.filename=""
        self.items4menu = {"File": [
            {MN:"Open", ID:102, HK:"&Open", KD:" Open a file to edit",
MF:self.OnOpen},
            {MN:"Save", ID:103, HK:"&Save", KD:" save file to disk",
MF:self.OnSave},
            {MN:"Edit", ID:104, HK:"&Edit", KD:" Do editing",
MF:self.OnEdit},
            {MN:"About", ID:101, HK:"&About", KD:" Information about this
program", MF:self.OnAbout},
            {MN:"Exit", ID:109, HK:"E&xit", KD:" Terminate the program",
MF:self.OnExit}
        ],  #END OF FILE MENU!
        "Voice": [
            {MN:"Read", ID:202, HK:"&Read", KD:" Open a file to read",
MF:self.OnWav2Read},
            {MN:"Save", ID:203, HK:"&Save", KD:" save text to audio file",
MF:self.OnSave2Wav},
            {MN:"Text", ID:204, HK:"Te&xt", KD:" read text field",
MF:self.OnRead},
            {MN:"Quit", ID:205, HK:"&Quit", KD:" Stop Reading",
MF:self.OnQuitRead}
        ],  #END OF VOICE MENU!
        "Settings": [
            {MN:"Voice", ID:302, HK:"&Speaker", KD:" Name for voice.",
MF:self.OnVoice, MF2:self.OnClick},
            {MN:"Rate", ID:303, HK:"&Rate", KD:" Rate for voice.",
MF:self.OnVoice, MF2:self.OnClick},
            {MN:"Pitch", ID:304, HK:"&Pitch", KD:" Pitch for voice.",
MF:self.OnVoice, MF2:self.OnClick},
            {MN:"Volume", ID:305, HK:"&Volume", KD:" Volume for voice.",
MF:self.OnVoice, MF2:self.OnClick}
        ],  #END OF SETTINGS MENU!
        "Down": [
            {MN:"Down", ID:DW_ID, HK:"&Down", KD:" Lower Setting.",
MF:self.OnVoice, MF2:self.OnClick}
        ]  #END OF DOWN MENU!
        }  #END OF ITEMS FOR MENU!
        self.buttons4voice = [
            {MN:"Voice", ID:111, HK:"&Voice", KD:" Voice Change",
MF:self.OnEnter, MF2:self.OnClick},
            {MN:"Rate", ID:112, HK:"&Rate", KD:" Rate For Voice Adjust",
MF:self.OnEnter, MF2:self.OnClick},
            {MN:"Pitch", ID:113, HK:"&Pitch", KD:" Pitch Of Voice Adjust",
MF:self.OnEnter, MF2:self.OnClick},
            {MN:"Volume", ID:114, HK:"&Loudness", KD:" Volume Loudness
Adjust", MF:self.OnEnter, MF2:self.OnClick}
        ]  #END OF ITEM FOR BUTTONS!
        wx.Frame.__init__(self, parent, wx.ID_ANY, title)
        self.control = wx.TextCtrl(self, 1, style=wx.TE_MULTILINE)
        self.CreateStatusBar()  #A Statusbar in the bottom of the window
        #Setting up the menu.
        filemenu = wx.Menu()
        for o in self.items4menu["File"]:
            filemenu.Append( o[ID], o[HK], o[KD])
            filemenu.AppendSeparator()
        voicemenu = wx.Menu()
        for o in self.items4menu["Voice"]:
            voicemenu.Append( o[ID], o[HK], o[KD])
            voicemenu.AppendSeparator()
        setting_menu = wx.Menu()
        for o in self.items4menu["Settings"]:
            down_menu = wx.Menu()
            down_menu.Append( o[ID], o[HK], o[KD])
            d = self.items4menu["Down"][0]
            down_menu.Append( d[ID]+o[ID], d[HK], d[KD])
            setting_menu.AppendMenu( o[ID], o[HK]+" Setting", down_menu)
            setting_menu.AppendSeparator()
        voicemenu.AppendMenu(-1, "&VoiceSettings", setting_menu)
        # Creating the menubar.
        menuBar = wx.MenuBar()
        menuBar.Append( filemenu,"&File") # Adding the "filemenu" to the
MenuBar
        menuBar.Append( voicemenu,"&Voice") # Adding the "voicemenu" to the
MenuBar
        self.SetMenuBar(menuBar)  # Adding the MenuBar to the Frame content.
        self.data4menu = {}
        for o in self.items4menu["File"]:
            wx.EVT_MENU(self, o[ID], o[MF])
            self.data4menu[ o[ID]] = o
        for o in self.items4menu["Voice"]:
            wx.EVT_MENU(self, o[ID], o[MF])
            self.data4menu[ o[ID]] = o
        for o in self.items4menu["Settings"]:
            wx.EVT_MENU(self, o[ID], o[MF])
            self.data4menu[ o[ID]] = o
            wx.EVT_MENU(self, o[ID]+DW_ID, o[MF])
            self.data4menu[ o[ID]+DW_ID] = o
        self.sizer2 = wx.BoxSizer(wx.HORIZONTAL)
        self.buttons=[]
        self.data4buttons={}
        i=0
        for o in self.buttons4voice:
            self.buttons.append( wx.Button(self, o[ID], o[HK]+o[KD]))
            self.sizer2.Add( self.buttons[i], 1, wx.EXPAND)
            self.data4buttons[ self.buttons[i].GetId()] = o
            self.buttons[i].Bind(wx.EVT_ENTER_WINDOW, o[MF]) #,
id=self.buttons[i].GetId())
            self.buttons[i].Bind( wx.EVT_RIGHT_DOWN, o[MF2]) #,
id=self.buttons[i].GetId())
            self.buttons[i].Bind( wx.EVT_BUTTON, o[MF2]) #,
id=self.buttons[i].GetId())
            i+=1
        # Use some sizers to see layout options
        self.sizer=wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.sizer2,0,wx.EXPAND)
        self.sizer.Add(self.control,1,wx.EXPAND)
        #Layout sizers
        self.SetSizer(self.sizer)
        self.SetAutoLayout(1)
        self.sizer.Fit(self)
        self.Show(1)
    def OnEnter(self, event):
        "WHEN ENTERING SAY LABEL OF BUTTON!"
#        self.button2bind = self.Bind(wx.EVT_BUTTON, self.OnClick,
id=event.GetId())
        eventType = event.GetEventType()
#        tts.Speak( event.GetPosition())
        label4btn = self.data4buttons[ event.GetId()][0]
#        tts.Speak( label4btn)
        if label4btn == "Voice":
            set_value = tts.getVoiceName()
        elif label4btn == "Rate":
            set_value = str( tts.getRate())
        elif label4btn == "Pitch":
            set_value = str( tts.getPitch())
        elif label4btn == "Volume":
            set_value = str( tts.getVolume())
        text = label4btn +" Button " +set_value
        tts.Speak( text, async, purge)
    def OnVoice(self, event):
        "CHECK IF DOWN KEY FOR VOICE SETTING!"
        wx.EVT_KEY_DOWN(self, self.OnClick)
        self.OnClick( event)
    def OnClick(self, event):
        "BUTTON CLICKED ON AND IN FUTURE GET BUTTON POS!"
#comment        tts.Speak( 'Event Name: %s Time Stamp: %s ' %
(event.GetClassName(), event.GetTimestamp()))
#comment        print 'Event Name: %s Time Stamp: %s ' %
(event.GetClassName(), event.GetTimestamp())
#comment IF KEY EVENT:
#comment        key = event.GetKeyCode()
#comment        pos = event.GetPositionTuple()
        eventType = event.GetEventType()
#comment        if eventType == wx.EVT_BUTTON: print "Mouse! "
        eventName = event.GetClassName()
        eventId = event.GetId()
#comment        self.control.SetValue( eventName)
#comment: CHECKING ID AND SETTING DIRECTION!
        if eventId in self.data4menu:
            label4btn = self.data4menu[ event.GetId()][0]
        else:
            label4btn = self.data4buttons[ event.GetId()][0]
        dir = 1
        if eventName == "wxMouseEvent" or eventId > DW_ID: dir = -1
        set_value = "Error! Wrong Button!"
        if label4btn == "Voice":
            value = tts.getVoiceNum() + dir
            if value < 1:               value += 0
            if value >= tts.getVoiceCount():
                value = tts.getVoiceCount()-1
            tts.setVoice( value)
            set_value = tts.getVoiceName()
        elif label4btn == "Rate":
            value = tts.getRate()
            value += dir
            if value > 10: value = 10
            if value < -10: value = -10
            tts.setRate( value)
            set_value = str( value)
        elif label4btn == "Pitch":
            value = tts.getPitch()
            value += dir
            if value > 10: value = 10
            if value < -10: value = -10
            tts.setPitch( value)
            set_value = str( value)
        elif label4btn == "Volume":
            value = tts.getVolume()
            value += 10*dir
            if value > 100: value = 100
            if value < 0:
                tts.setVolume( 50)
                tts.Speak( "Volume Minimum!")
                value = 0
            tts.setVolume( value)
            set_value = str( value)
        tts.Speak( set_value, async)
    def OnAbout(self,e):
        "A dialog box saying what the editor is about!"
        text = " A sample editor with voice \n in wxPython."
        tts.Speak( "About A Sample Editor!"+text)
        d= wx.MessageDialog( self, text, "About Sample Editor", wx.OK)
                            # Create a message dialog box
        d.ShowModal() # Shows it
        d.Destroy() # finally destroy it when finished.
    def OnExit(self,e):
        self.Close(True)  # Close the frame.
    def OnOpen(self,e):
        """ Open a file"""
        self.setFilePath( "o")
        f=open(os.path.join(self.dirname, self.filename),'r')
        self.control.SetValue(f.read())
        f.close()
    def OnSave(self,e):
        """ Save a file"""
        self.setFilePath( "s")
        f=open(os.path.join(self.dirname, self.filename),'w')
#       self.control.SetValue(f.read())
        f.write( self.control.GetValue())
        f.close()
    def setFilePath(self, type4file=""):
        " Search directory for path and file name!"
        fn=self.filename
        t4f = wx.OPEN
        if type4file[0] in "sS":
            t4f = wx.SAVE|wx.FD_OVERWRITE_PROMPT
#            fn = self.filename
        dlg = wx.FileDialog(self, self.filename +" or Choose a file",
self.dirname, fn, "*.*", t4f)
        if dlg.ShowModal() == wx.ID_OK:
            self.filename = dlg.GetFilename()
            self.dirname = dlg.GetDirectory()
        dlg.Destroy()
    def OnWav2Read(self,e):
        """ Open a file to read"""
        self.setFilePath( "o")
        tts.SpeakFromWav( os.path.join(self.dirname, self.filename), async,
purge)
    def OnSave2Wav(self,e):
        """ Save text to a wav file"""
        self.setFilePath( "s")
        tts.SpeakToWav( os.path.join(self.dirname, self.filename),
self.control.GetValue())
    def OnRead(self,e):
        """ Read the text"""
        tts.Speak( self.control.GetValue(), async, purge)
    def OnQuitRead(self,e):
        """ Quit the reading of the text"""
        tts.Speak( " Talking Stopped!", purge)
    def OnEdit(self,e):
        """ Edit the file"""
        self.control.SetFocus()
app = wx.PySimpleApp()
frame = MainWindow(None, -1, "Sample editor")
app.MainLoop()
desc="""lay out the six buttons into two rows of three columns each -- to do that, you should
use a wxGridSizer.
In the following example we use two nested sizers, the main one with vertical layout
and the embedded one with horizontal layout:"""
#Editor.py
import wx
import os
import Sapi5
tts = Sapi5.Create( {"name":"Mary"})
purge = tts._purge
async = tts._async
punc = tts._punc
MN=0
ID=1
HK=2
KD=3
MF=4
MF2=5
DW_ID=1000
class MainWindow(wx.Frame):
    def __init__(self, parent, id, title):
        self.dirname=os.getcwd()  #SEARCH FROM PRESENT DIRECTORY!
        self.filename=""
        self.items4menu = {"File": [
            {MN:"Open", ID:102, HK:"&Open", KD:" Open a file to edit", MF:self.OnOpen},
            {MN:"Save", ID:103, HK:"&Save", KD:" save file to disk", MF:self.OnSave},
            {MN:"Edit", ID:104, HK:"&Edit", KD:" Do editing", MF:self.OnEdit},
            {MN:"About", ID:101, HK:"&About", KD:" Information about this program", MF:self.OnAbout},
            {MN:"Exit", ID:109, HK:"E&xit", KD:" Terminate the program", MF:self.OnExit}
        ],  #END OF FILE MENU!
        "Voice": [
            {MN:"Read", ID:202, HK:"&Read", KD:" Open a file to read", MF:self.OnWav2Read},
            {MN:"Save", ID:203, HK:"&Save", KD:" save text to audio file", MF:self.OnSave2Wav},
            {MN:"Text", ID:204, HK:"Te&xt", KD:" read text field", MF:self.OnRead},
            {MN:"Quit", ID:205, HK:"&Quit", KD:" Stop Reading", MF:self.OnQuitRead}
        ],  #END OF VOICE MENU!
        "Settings": [
            {MN:"Voice", ID:302, HK:"&Speaker", KD:" Name for voice.", MF:self.OnVoice, MF2:self.OnClick},
            {MN:"Rate", ID:303, HK:"&Rate", KD:" Rate for voice.", MF:self.OnVoice, MF2:self.OnClick},
            {MN:"Pitch", ID:304, HK:"&Pitch", KD:" Pitch for voice.", MF:self.OnVoice, MF2:self.OnClick},
            {MN:"Volume", ID:305, HK:"&Volume", KD:" Volume for voice.", MF:self.OnVoice, MF2:self.OnClick}
        ],  #END OF SETTINGS MENU!
        "Down": [
            {MN:"Down", ID:DW_ID, HK:"&Down", KD:" Lower Setting.", MF:self.OnVoice, MF2:self.OnClick}
        ]  #END OF DOWN MENU!
        }  #END OF ITEMS FOR MENU!
        self.buttons4voice = [
            {MN:"Voice", ID:111, HK:"&Voice", KD:" Voice Change", MF:self.OnEnter, MF2:self.OnClick},
            {MN:"Rate", ID:112, HK:"&Rate", KD:" Rate For Voice Adjust", MF:self.OnEnter, MF2:self.OnClick},
            {MN:"Pitch", ID:113, HK:"&Pitch", KD:" Pitch Of Voice Adjust", MF:self.OnEnter, MF2:self.OnClick},
            {MN:"Volume", ID:114, HK:"&Loudness", KD:" Volume Loudness Adjust", MF:self.OnEnter, MF2:self.OnClick}
        ]  #END OF ITEM FOR BUTTONS!
        wx.Frame.__init__(self, parent, wx.ID_ANY, title)
        self.control = wx.TextCtrl(self, 1, style=wx.TE_MULTILINE)
        self.CreateStatusBar()  #A Statusbar in the bottom of the window
        #Setting up the menu.
        filemenu = wx.Menu()
        for o in self.items4menu["File"]:
            filemenu.Append( o[ID], o[HK], o[KD])
            filemenu.AppendSeparator()
        voicemenu = wx.Menu()
        for o in self.items4menu["Voice"]:
            voicemenu.Append( o[ID], o[HK], o[KD])
            voicemenu.AppendSeparator()
        setting_menu = wx.Menu()
        for o in self.items4menu["Settings"]:
            down_menu = wx.Menu()
            down_menu.Append( o[ID], o[HK], o[KD])
            d = self.items4menu["Down"][0]
            down_menu.Append( d[ID]+o[ID], d[HK], d[KD])
            setting_menu.AppendMenu( o[ID], o[HK]+" Setting", down_menu)
            setting_menu.AppendSeparator()
        voicemenu.AppendMenu(-1, "&VoiceSettings", setting_menu)
        # Creating the menubar.
        menuBar = wx.MenuBar()
        menuBar.Append( filemenu,"&File") # Adding the "filemenu" to the MenuBar
        menuBar.Append( voicemenu,"&Voice") # Adding the "voicemenu" to the MenuBar
        self.SetMenuBar(menuBar)  # Adding the MenuBar to the Frame content.
        self.data4menu = {}
        for o in self.items4menu["File"]:
            wx.EVT_MENU(self, o[ID], o[MF])
            self.data4menu[ o[ID]] = o
        for o in self.items4menu["Voice"]:
            wx.EVT_MENU(self, o[ID], o[MF])
            self.data4menu[ o[ID]] = o
        for o in self.items4menu["Settings"]:
            wx.EVT_MENU(self, o[ID], o[MF])
            self.data4menu[ o[ID]] = o
            wx.EVT_MENU(self, o[ID]+DW_ID, o[MF])
            self.data4menu[ o[ID]+DW_ID] = o
        self.sizer2 = wx.BoxSizer(wx.HORIZONTAL)
        self.buttons=[]
        self.data4buttons={}
        i=0
        for o in self.buttons4voice:
            self.buttons.append( wx.Button(self, o[ID], o[HK]+o[KD]))
            self.sizer2.Add( self.buttons[i], 1, wx.EXPAND)
            self.data4buttons[ self.buttons[i].GetId()] = o
            self.buttons[i].Bind(wx.EVT_ENTER_WINDOW, o[MF]) #, id=self.buttons[i].GetId())
            self.buttons[i].Bind( wx.EVT_RIGHT_DOWN, o[MF2]) #, id=self.buttons[i].GetId())
            self.buttons[i].Bind( wx.EVT_BUTTON, o[MF2]) #, id=self.buttons[i].GetId())
            i+=1
        # Use some sizers to see layout options
        self.sizer=wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.sizer2,0,wx.EXPAND)
        self.sizer.Add(self.control,1,wx.EXPAND)
        #Layout sizers
        self.SetSizer(self.sizer)
        self.SetAutoLayout(1)
        self.sizer.Fit(self)
        self.Show(1)
    def OnEnter(self, event):
        "WHEN ENTERING SAY LABEL OF BUTTON!"
#        self.button2bind = self.Bind(wx.EVT_BUTTON, self.OnClick, id=event.GetId())
        eventType = event.GetEventType()
#        tts.Speak( event.GetPosition())
        label4btn = self.data4buttons[ event.GetId()][0]
#        tts.Speak( label4btn)
        if label4btn == "Voice":
            set_value = tts.getVoiceName()
        elif label4btn == "Rate":
            set_value = str( tts.getRate())
        elif label4btn == "Pitch":
            set_value = str( tts.getPitch())
        elif label4btn == "Volume":
            set_value = str( tts.getVolume())
        text = label4btn +" Button " +set_value
        tts.Speak( text, async, purge)
    def OnVoice(self, event):
        "CHECK IF DOWN KEY FOR VOICE SETTING!"
        wx.EVT_KEY_DOWN(self, self.OnClick)
        self.OnClick( event)
    def OnClick(self, event):
        "BUTTON CLICKED ON AND IN FUTURE GET BUTTON POS!"
#comment        tts.Speak( 'Event Name: %s Time Stamp: %s ' % (event.GetClassName(), event.GetTimestamp()))
#comment        print 'Event Name: %s Time Stamp: %s ' % (event.GetClassName(), event.GetTimestamp())
#comment IF KEY EVENT:
#comment        key = event.GetKeyCode()
#comment        pos = event.GetPositionTuple()
        eventType = event.GetEventType()
#comment        if eventType == wx.EVT_BUTTON: print "Mouse! "
        eventName = event.GetClassName()
        eventId = event.GetId()
#comment        self.control.SetValue( eventName)
#comment: CHECKING ID AND SETTING DIRECTION!
        if eventId in self.data4menu:
            label4btn = self.data4menu[ event.GetId()][0]
        else:
            label4btn = self.data4buttons[ event.GetId()][0]
        dir = 1
        if eventName == "wxMouseEvent" or eventId > DW_ID: dir = -1
        set_value = "Error! Wrong Button!"
        if label4btn == "Voice":
            value = tts.getVoiceNum() + dir
            if value < 1:               value += 0
            if value >= tts.getVoiceCount():
                value = tts.getVoiceCount()-1
            tts.setVoice( value)
            set_value = tts.getVoiceName()
        elif label4btn == "Rate":
            value = tts.getRate()
            value += dir
            if value > 10: value = 10
            if value < -10: value = -10
            tts.setRate( value)
            set_value = str( value)
        elif label4btn == "Pitch":
            value = tts.getPitch()
            value += dir
            if value > 10: value = 10
            if value < -10: value = -10
            tts.setPitch( value)
            set_value = str( value)
        elif label4btn == "Volume":
            value = tts.getVolume()
            value += 10*dir
            if value > 100: value = 100
            if value < 0:
                tts.setVolume( 50)
                tts.Speak( "Volume Minimum!")
                value = 0
            tts.setVolume( value)
            set_value = str( value)
        tts.Speak( set_value, async)
    def OnAbout(self,e):
        "A dialog box saying what the editor is about!"
        text = " A sample editor with voice \n in wxPython."
        tts.Speak( "About A Sample Editor!"+text)
        d= wx.MessageDialog( self, text, "About Sample Editor", wx.OK)
                            # Create a message dialog box
        d.ShowModal() # Shows it
        d.Destroy() # finally destroy it when finished.
    def OnExit(self,e):
        self.Close(True)  # Close the frame.
    def OnOpen(self,e):
        """ Open a file"""
        self.setFilePath( "o")
        f=open(os.path.join(self.dirname, self.filename),'r')
        self.control.SetValue(f.read())
        f.close()
    def OnSave(self,e):
        """ Save a file"""
        self.setFilePath( "s")
        f=open(os.path.join(self.dirname, self.filename),'w')
#       self.control.SetValue(f.read())
        f.write( self.control.GetValue())
        f.close()
    def setFilePath(self, type4file=""):
        " Search directory for path and file name!"
        fn=self.filename
        t4f = wx.OPEN
        if type4file[0] in "sS":
            t4f = wx.SAVE|wx.FD_OVERWRITE_PROMPT
#            fn = self.filename
        dlg = wx.FileDialog(self, self.filename +" or Choose a file", self.dirname, fn, "*.*", t4f)
        if dlg.ShowModal() == wx.ID_OK:
            self.filename = dlg.GetFilename()
            self.dirname = dlg.GetDirectory()
        dlg.Destroy()
    def OnWav2Read(self,e):
        """ Open a file to read"""
        self.setFilePath( "o")
        tts.SpeakFromWav( os.path.join(self.dirname, self.filename), async, purge)
    def OnSave2Wav(self,e):
        """ Save text to a wav file"""
        self.setFilePath( "s")
        tts.SpeakToWav( os.path.join(self.dirname, self.filename), self.control.GetValue())
    def OnRead(self,e):
        """ Read the text"""
        tts.Speak( self.control.GetValue(), async, purge)
    def OnQuitRead(self,e):
        """ Quit the reading of the text"""
        tts.Speak( " Talking Stopped!", purge)
    def OnEdit(self,e):
        """ Edit the file"""
        self.control.SetFocus()
app = wx.PySimpleApp()
frame = MainWindow(None, -1, "Sample editor")
app.MainLoop()
desc2="""The sizer.Add
 method has three arguments. The first one specifies the control to include in the
sizer. The second one is a weight factor which means that this control will be sized
in proportion to other ones. For example, if you had three edit controls and you
wanted them to have the proportions 3:2:1 then you would specify these factors as
arguments when adding the controls. 0 means that this control or sizer will not grow.
The third argument is normally
wxGROW (same as wxEXPAND
) which means the control will be resized when necessary. If you use
wxSHAPED instead, the controls aspect ratio will remain the same.
If the second parameter is 0, i.e. the control will not be resized, the third parameter
may indicate if the control should be centered horizontally and/or vertically by
using wxALIGN_CENTER_HORIZONTAL, wxALIGN_CENTER_VERTICAL, or wxALIGN_CENTER
 (for both) instead of
wxGROW or wxSHAPED as that third parameter.
You can alternatively specify combinations of wxALIGN_LEFT, wxALIGN_TOP, wxALIGN_RIGHT,
and wxALIGN_BOTTOM. The default behavior is equivalent to
wxALIGN_LEFT | wxALIGN_TOP."""
#DRIVERS FOR SAPI 5 AND VOICES!
#NOTE THE CONSTANTS AND IN THE SPEAK FUNCTION AND THE ADDING/OR OF THE VALUES.
from comtypes.client import CreateObject
import _winreg

class constants4tts:
    Wait = -1
    Sync = 0
    Async = 1
    Purge = 2
    Is_filename = 4
    XML = 8
    Not_XML = 16
    Persist = 32
    Punc = 64

class SynthDriver():
    name="sapi5"
    description="Microsoft Speech API version 5 (sapi.SPVoice)"
    _voice = 0
    _pitch = 0
    _voices = []
    _wait = -1 #WAIT INDEFINITELY
    _sync = 0 #WAIT UNTIL SPEECH IS DONE.
    _async = 1 #DO NOT WAIT FOR SPEECH
    _purge = 2 #CLEAR SPEAKING BUFFER
    _is_filename = 4 #OPEN WAV FILE TO SPEAK OR SAVE TO WAV FILE
    _xml = 8 #XML COMMANDS, PRONUNCIATION AND GRAMMER.
    _not_xml = 16 #NO XML COMMANDS
    _persist_xml = 32 #Changes made in one speak command persist to other calls to Speak.
    _punc = 64 #PRONOUNCE ALL PUNCTUATION!
    def check(self):
        try:
            r=_winreg.OpenKey(_winreg.HKEY_CLASSES_ROOT,"SAPI.SPVoice")
            r.Close()
            return True
        except:
            return False
#INITIALIZE ENGINE!
    def init(self):
        try:
            self.tts = CreateObject( 'sapi.SPVoice')
            self._voice=0
            self._voiceCount = len(self.tts.GetVoices())
            for v in range(self._voiceCount):
                self._voices.append( self.tts.GetVoices()[v])
            return True
        except:
            return False
#TERMINATE INSTANCE OF ENGINE!
    def terminate(self):
        del self.tts
#NUMBER OF VOICES FOR ENGINE!
    def getVoiceCount(self):
        return len(self.tts.GetVoices())
#NAME OF A VOICE BY NUMBER!
    def getVoiceNameByNum(self, num):
        return self.tts.GetVoices()[ num].GetDescription()
#NAME OF A VOICE!
    def getVoiceName(self):
        return self.tts.GetVoices()[ self._voice].GetDescription()
#WHAT IS VOICE RATE?
    def getRate(self):
        "MICROSOFT SAPI 5 RATE IS -10 TO 10"
        return (self.tts.rate)
#WHAT IS THE VOICE PITCH?
    def getPitch(self):
        "PITCH FOR MICROSOFT SAPI 5 IS AN XML COMMAND!"
        return self._pitch
#GET THE ENGINE VOLUME!
    def getVolume(self):
        "MICROSOFT SAPI 5 VOLUME IS 1% TO 100%"
        return self.tts.volume
#GET THE VOICE NUMBER!
    def getVoiceNum(self):
        return self._voice
#SET A VOICE BY NAME!
    def setVoiceByName(self, name=""):
        "VOICE IS SET BY NAME!"
        for i in range( self._voiceCount):
            if name and self.tts.GetVoices()[ i].GetDescription().find( name) >= 0:
                self.tts.Voice = self._voices[i]
#                self.tts.Speak( "%s Set!" % name)
                self._voice=i
                break
        if i >= self._voiceCount:
            self.tts.Speak( "%s Name Not Found!" % name)
#USED FOR BOOKMARKING AND USE LATER!
    def _get_lastIndex(self):
        bookmark=self.tts.status.LastBookmark
        if bookmark!="" and bookmark is not None:
            return int(bookmark)
        else:
            return -1
#NOW SET ENGINE PARMS!
#SET THE VOICE RATE!
    def setRate(self, rate):
        "MICROSOFT SAPI 5 RATE IS -10 TO 10"
        if rate > 10: rate = 10
        if rate < -10: rate = -10
        self.tts.Rate = rate
#SET PITCH OF THE VOICE!
    def setPitch(self, value):
        "MICROSOFT SAPI 5 pitch is really controled with xml around speECH TEXT AND IS -10 TO 10"
        if value > 10: value = 10
        if value < -10: value = -10
        self._pitch=value
#SET THE VOICE VOLUME!
    def setVolume(self, value):
        "MICROSOFT SAPI 5 VOLUME IS 1% TO 100%"
        self.tts.Volume = value
#CREATE ANOTHER INSTANCE OF A VOICE!
    def createVoice(self, name=""):
        num = self._voice
        for i in range( self._voiceCount):
            if name and self.tts.GetVoices()[ i].GetDescription().find( name) >= 0:
                num=i
                break
        new_tts = CreateObject( 'sapi.SPVoice')
        new_tts.Voice = self._voices[ num]
        return (new_tts)
#SPEAKING TEXT!
#SPEAK TEXT USING BOOKMARKS AND PITCH!
    def SpeakText(self, text, wait=False, index=None):
        "SPEAK TEXT AND XML FOR PITCH MUST REPLACE ANY <> SYMBOLS BEFORE USING XML BRACKETED TEXT"
        flags = constants4tts.XML
        text = text.replace( "<", "&lt;")
        pitch = ((self._pitch*2)-100)/10
        if isinstance(index, int):
            bookmarkXML = "<Bookmark Mark = \"%d\" />" % index #NOTE \" FOR XML FORMAT CONVERSION!
        else:
            bookmarkXML = ""
        flags = constants4tts.XML
        if wait is False:
            flags += constants4tts.Async
        self.tts.Speak( "<pitchabsmiddle = \"%s\">%s%s</pitch>" % (pitch, bookmarkXML, text), flags)
#CANCEL SPEAK IN PROGRESS!
    def cancel(self):
        #if self.tts.Status.RunningState == 2:
        self.tts.Speak(None, 1|constants4tts.Purge)
#SET AUDIO STREAM FOR OUTPUT TO A FILE!
    def SpeakToWav(self, filename, text, voice=""):
        """THIS METHOD ASSUMES THE IMPORT OF COMTYPES.CLIENT createObject SO
            A VOICE AND FILE STREAM OBJECT ARE CREATED WITH THE PASSING IN OF 3 STRINGS:
            THE FILE NAME TO SAVE THE VOICE INTO, THE TEXT, AND THE VOICE SPOKEN IN.
            ONCE THE TEXT IS SPOKEN INTO THE FILE, IT IS CLOSED AND THE OBJECTS DESTROYED!"""
        num = self._voice
        for i in range( self._voiceCount):
            if voice and self.tts.GetVoices()[ i].GetDescription().find( voice) >= 0:
                num=i
                break
        stream = CreateObject("SAPI.SpFileStream")
        tts4file = CreateObject( 'sapi.SPVoice')
        tts4file.Voice = self._voices[ num]
        from comtypes.gen import SpeechLib
        stream.Open( filename, SpeechLib.SSFMCreateForWrite)
        tts4file.AudioOutputStream = stream
        tts4file.Speak( text, 0)
        stream.Close()
        del tts4file
        del stream
#NOW SPEAK THE WAV FILE SAVED!
    def SpeakFromWav(self, filename, sync=0, async=0, purge=0):
        "SPEAKING A WAV FILE ONLY!"
        self.tts.Speak( filename, sync |async |purge |self._is_filename)
#SPEAK TEXT!
    def Speak(self, text, wait=0, sync=0, async=0, purge=0, isfile=0, xml=0, not_xml=0, persist=0, punc=0):
        "SAPI 5 HAS NO PITCH SO HAS TO BE IN TEXT SPOKEN!"
        pitch=self._pitch
        self.tts.Speak( "<pitch absmiddle='%s'/>%s" % (pitch, text), wait |sync |async |purge |isfile |xml |not_xml |persist |punc)
#SPEAK TEXT WITHOUT PITCH!
    def Speaking(self, text, wait=0, sync=0, async=0, purge=0, isfile=0, xml=0, not_xml=0, persist=0, punc=0):
        "SPEAKING A FILE WITHOUT PITCH"
        self.tts.Speak( text, wait |sync |async |purge |isfile |xml |not_xml |persist |punc)
#SET THE VOICE BY VALUE!
    def setVoice(self, value):
        """SET VOICE BY NUMBER OR VALUE!"""
        if value >= self._voiceCount:
            value = self._voiceCount-1
        if value < 1:
            value=0
        self.tts.Voice = self._voices[ value]
#        vd = self.tts.GetVoices()[ value].GetDescription()
#        self.tts.Speak( vd[ vd.find(" ")+1:])
        self._voice=value
#READ ALL THE VOICES IN THE ENGINE!
    def read_Voices(self):
        self.tts.Speak( "Voices are:")
        for i in range( self._voiceCount):
            print "%d) %s" % (i, self.getVoiceNameByNum(i))
            self.tts.Voice = self.tts.GetVoices()[i]
            vd = self.tts.GetVoices()[ i].GetDescription()
            self.tts.Speak( "%d) %s" % (i, vd[ vd.find(" ")+1:]))

def Create( vs={}):
    "CREATE A SAPI VOICE INSTANCE!"
    vp = {"name":"Sam", "volume":100, "rate":0, "pitch":0}
    for i in vs: vp[i] = vs[i]
    newVoice = SynthDriver()
    if newVoice.check():
        newVoice.init()
        newVoice.setVoiceByName( vp["name"])
        newVoice.setVolume( vp["volume"])
        newVoice.setRate( vp["rate"])
        newVoice.setPitch( vp["pitch"])
        return newVoice
    else:
        print "SAPI Engine Is Not Installed On This Computer!"
        return Null