Changeset 2482

Show
Ignore:
Timestamp:
11/09/08 11:49:07 (2 months ago)
Author:
bzr
Message:

Merge ticket19 branch:

  • change: Voices are now stored in the configuration file by ID instead of by index. This makes voice settings more reliable across systems and configuration changes. The voice setting will not be preserved in old configurations and an error may be logged the first time a synthesizer is used. (#19)
  • fix: The eSpeak synthesizer driver no longer stops speaking completely after a single error.
  • fix: Fix the issue whereby updated voice parameters (rate, pitch, etc.) were not saved when the voice was changed from the synthesizer settings ring.
  • Some minor fixes to the sapi4 driver.
Location:
trunk
Files:
11 modified

Legend:

Unmodified
Added
Removed
  • trunk

    • Property bzr:revision-info
      •  

        old new  
        1 timestamp: 2008-11-06 17:51:53.163000107 +1000 
         1timestamp: 2008-11-09 22:45:27.869999886 +1100 
        22committer: James Teh <jamie@jantrid.net> 
        33properties:  
    • Property bzr:ancestry:v3-list-QlpoOTFBWSZTWbrL2vUAAB1VgAAQABCAQDrrnqAgAFCgaaGRkxBoTIJ6mmaNRwhndFAoNhZjh_YY4a01fOg1ulgNNC2UrzPdXXEnDpX8XckU4UJC6y9r1A..
      •  

        old new  
        1212jamie@jantrid.net-20081022055144-zd6v96gjzz94ript 
        1313jamie@jantrid.net-20081104100702-aqivw5dfzmygyo17 
         14jamie@jantrid.net-20081109113343-zabfejnnivsxf2gd 
    • Property bzr:revision-id:v3-list-QlpoOTFBWSZTWbrL2vUAAB1VgAAQABCAQDrrnqAgAFCgaaGRkxBoTIJ6mmaNRwhndFAoNhZjh_YY4a01fOg1ulgNNC2UrzPdXXEnDpX8XckU4UJC6y9r1A..
      •  

        old new  
        2512512279 jamie@jantrid.net-20081106071205-mve6ffhfpwjrngz8 
        2522522280 jamie@jantrid.net-20081106075153-p4o534murjkdzapb 
         2532281 jamie@jantrid.net-20081109114527-f0kqf0o8k8713bsc 
    • Property bzr:file-ids
      •  

        old new  
        1 source/generate.py      343@dbe06fc7-9119-0410-a01d-9dbf589ecbba:trunk:source%2Fgenerate.py 
         1source/config/__init__.py       46@dbe06fc7-9119-0410-a01d-9dbf589ecbba:trunk:source%2Fconfig%2F__init__.py 
         2source/gui/settingsDialogs.py   299@dbe06fc7-9119-0410-a01d-9dbf589ecbba:trunk:source%2Fgui%2FsettingsDialogs.py 
         3source/synthDriverHandler.py    97@dbe06fc7-9119-0410-a01d-9dbf589ecbba:trunk:source%2FsynthDriverHandler.py 
         4source/synthDrivers/_espeak.py  612@dbe06fc7-9119-0410-a01d-9dbf589ecbba:trunk:source%2FsynthDrivers%2F_espeak.py 
         5source/synthDrivers/audiologic.py       audiologic.py-20080701031200-sjkq3p9snm96sxhq-2 
         6source/synthDrivers/espeak.py   612@dbe06fc7-9119-0410-a01d-9dbf589ecbba:trunk:source%2FsynthDrivers%2Fespeak.py 
         7source/synthDrivers/sapi4.py    sapi4.py-20081105062308-fz5mgli5iso5vcdq-2 
         8source/synthDrivers/sapi5.py    94@dbe06fc7-9119-0410-a01d-9dbf589ecbba:trunk:source%2FsynthDrivers%2Fsapi5.py 
         9source/synthSettingsRing.py     1111@dbe06fc7-9119-0410-a01d-9dbf589ecbba:trunk:source%2F_synthSettingsRing.py 
         10user_docs/whats%20new.txt       559@dbe06fc7-9119-0410-a01d-9dbf589ecbba:trunk:user_docs%2Fwhats%20new.txt 
    • Property bzr:text-parents
      •  

        old new  
         1source/config/__init__.py       mick@kulgan.net-20081106053934-wy7im3e0a4n01gd5 
         2source/gui/settingsDialogs.py   jamie@jantrid.net-20081109091405-cr7i6qx0r32qxzv2 
         3source/synthDriverHandler.py    jamie@jantrid.net-20081109113343-zabfejnnivsxf2gd 
         4source/synthDrivers/_espeak.py  jamie@jantrid.net-20081106064553-6iftm4vexioo630i 
         5source/synthDrivers/audiologic.py       jamie@jantrid.net-20081109052259-kxgyslupb8qa1jye 
         6source/synthDrivers/espeak.py   jamie@jantrid.net-20081109052655-7z096nch59fgwzl5 
         7source/synthDrivers/sapi4.py    mick@kulgan.net-20081109055011-8tcy3vfb69sjdwge 
         8source/synthDrivers/sapi5.py    jamie@jantrid.net-20081109052259-kxgyslupb8qa1jye 
         9source/synthSettingsRing.py     jamie@jantrid.net-20081109100954-vzi0052zk64nib6y 
         10user_docs/whats%20new.txt       jamie@jantrid.net-20081109105054-ollwznuzdm6qy6ho 
  • trunk/source/config/__init__.py

    r2468 r2482  
    4343                capPitchChange = integer(default=30,min=-100,max=100) 
    4444                volume = integer(default=100,min=0,max=100) 
    45                 voice = integer(default=1,min=1) 
     45                voice = string() 
    4646                raisePitchForCapitals = boolean(default=true) 
    4747                sayCapForCapitals = boolean(default=false) 
  • trunk/source/gui/settingsDialogs.py

    r2470 r2482  
    208208        def makeSettings(self, settingsSizer): 
    209209                if getSynth().hasVoice: 
    210                         voiceListSizer=wx.BoxSizer(wx.HORIZONTAL) 
    211                         voiceListLabel=wx.StaticText(self,-1,label=_("&Voice")) 
    212                         voiceListID=wx.NewId() 
    213                         self.voiceList=wx.Choice(self,voiceListID,name="Voice:",choices=[getSynth().getVoiceName(x) for x in range(1,getSynth().voiceCount+1)]) 
     210                        sizer=wx.BoxSizer(wx.HORIZONTAL) 
     211                        label=wx.StaticText(self,wx.ID_ANY,label=_("&Voice")) 
     212                        self._voices=getSynth().availableVoices 
     213                        self.voiceList=wx.Choice(self,wx.ID_ANY,name="Voice:",choices=[x.name for x in self._voices]) 
    214214                        try: 
    215                                 voiceIndex=getSynth().voice 
    216                                 voiceIndex-=1 
    217                                 if voiceIndex>=0 and voiceIndex<getSynth().voiceCount: 
    218                                         self.voiceList.SetSelection(voiceIndex) 
    219                         except: 
     215                                curVoice=getSynth().voice 
     216                                voiceIndex=[x.ID for x in self._voices].index(curVoice) 
     217                                self.voiceList.SetSelection(voiceIndex) 
     218                        except ValueError: 
    220219                                pass 
    221220                        self.voiceList.Bind(wx.EVT_CHOICE,self.onVoiceChange) 
    222                         voiceListSizer.Add(voiceListLabel) 
    223                         voiceListSizer.Add(self.voiceList) 
    224                         settingsSizer.Add(voiceListSizer,border=10,flag=wx.BOTTOM) 
     221                        sizer.Add(label) 
     222                        sizer.Add(self.voiceList) 
     223                        settingsSizer.Add(sizer,border=10,flag=wx.BOTTOM) 
    225224                if getSynth().hasVariant: 
    226                         variantListSizer=wx.BoxSizer(wx.HORIZONTAL) 
    227                         variantListLabel=wx.StaticText(self,-1,label=_("V&ariant")) 
    228                         variantListID=wx.NewId() 
    229                         self.variantList=wx.Choice(self,variantListID,name="Variant:",choices=[getSynth().getVariantName(x) for x in range(0,getSynth().variantCount)]) 
    230                         currentVariant=getSynth().variant 
    231                         for x in range(0,getSynth().variantCount): 
    232                                 if currentVariant==getSynth().getVariantIdentifier(x): 
    233                                         break 
    234                         self.variantList.SetSelection(x) 
     225                        sizer=wx.BoxSizer(wx.HORIZONTAL) 
     226                        label=wx.StaticText(self,wx.ID_ANY,label=_("V&ariant")) 
     227                        self._variants=getSynth().availableVariants 
     228                        self.variantList=wx.Choice(self,wx.ID_ANY,name="Variant:",choices=[x.name for x in self._variants]) 
     229                        try: 
     230                                curVariant=getSynth().variant 
     231                                variantIndex=[x.ID for x in self._variants].index(curVariant) 
     232                                self.variantList.SetSelection(variantIndex) 
     233                        except ValueError: 
     234                                pass 
    235235                        self.variantList.Bind(wx.EVT_CHOICE,self.onVariantChange) 
    236                         variantListSizer.Add(variantListLabel) 
    237                         variantListSizer.Add(self.variantList) 
    238                         settingsSizer.Add(variantListSizer,border=10,flag=wx.BOTTOM) 
    239                 if getSynth().hasRate: 
    240                         rateSliderSizer=wx.BoxSizer(wx.HORIZONTAL) 
    241                         rateSliderLabel=wx.StaticText(self,-1,label=_("&Rate")) 
    242                         rateSliderID=wx.NewId() 
    243                         self.rateSlider=wx.Slider(self,rateSliderID,value=getSynth().rate,minValue=0,maxValue=100,name="Rate:") 
    244                         self._setSliderStepSizes(self.rateSlider,getSynth().rateMinStep) 
    245                         self.rateSlider.Bind(wx.EVT_SLIDER,self.onRateChange) 
    246                         rateSliderSizer.Add(rateSliderLabel) 
    247                         rateSliderSizer.Add(self.rateSlider) 
    248                         settingsSizer.Add(rateSliderSizer,border=10,flag=wx.BOTTOM) 
    249                 if getSynth().hasPitch: 
    250                         pitchSliderSizer=wx.BoxSizer(wx.HORIZONTAL) 
    251                         pitchSliderLabel=wx.StaticText(self,-1,label=_("&Pitch")) 
    252                         pitchSliderID=wx.NewId() 
    253                         self.pitchSlider=wx.Slider(self,pitchSliderID,value=getSynth().pitch,minValue=0,maxValue=100) 
    254                         self._setSliderStepSizes(self.pitchSlider,getSynth().pitchMinStep) 
    255                         self.pitchSlider.Bind(wx.EVT_SLIDER,self.onPitchChange) 
    256                         pitchSliderSizer.Add(pitchSliderLabel) 
    257                         pitchSliderSizer.Add(self.pitchSlider) 
    258                         settingsSizer.Add(pitchSliderSizer,border=10,flag=wx.BOTTOM) 
    259                 if getSynth().hasInflection: 
    260                         inflectionSliderSizer=wx.BoxSizer(wx.HORIZONTAL) 
    261                         inflectionSliderLabel=wx.StaticText(self,-1,label=_("&Inflection")) 
    262                         inflectionSliderID=wx.NewId() 
    263                         self.inflectionSlider=wx.Slider(self,inflectionSliderID,value=getSynth().inflection,minValue=0,maxValue=100) 
    264                         self._setSliderStepSizes(self.inflectionSlider,getSynth().inflectionMinStep) 
    265                         self.inflectionSlider.Bind(wx.EVT_SLIDER,self.onInflectionChange) 
    266                         inflectionSliderSizer.Add(inflectionSliderLabel) 
    267                         inflectionSliderSizer.Add(self.inflectionSlider) 
    268                         settingsSizer.Add(inflectionSliderSizer,border=10,flag=wx.BOTTOM) 
    269                 if getSynth().hasVolume: 
    270                         volumeSliderSizer=wx.BoxSizer(wx.HORIZONTAL) 
    271                         volumeSliderLabel=wx.StaticText(self,-1,label=_("V&olume")) 
    272                         volumeSliderID=wx.NewId() 
    273                         self.volumeSlider=wx.Slider(self,volumeSliderID,value=getSynth().volume,minValue=0,maxValue=100) 
    274                         self._setSliderStepSizes(self.volumeSlider,getSynth().volumeMinStep) 
    275                         self.volumeSlider.Bind(wx.EVT_SLIDER,self.onVolumeChange) 
    276                         volumeSliderSizer.Add(volumeSliderLabel) 
    277                         volumeSliderSizer.Add(self.volumeSlider) 
    278                         settingsSizer.Add(volumeSliderSizer,border=10,flag=wx.BOTTOM) 
     236                        sizer.Add(label) 
     237                        sizer.Add(self.variantList) 
     238                        settingsSizer.Add(sizer,border=10,flag=wx.BOTTOM) 
     239                sizer=wx.BoxSizer(wx.HORIZONTAL) 
     240                label=wx.StaticText(self,wx.ID_ANY,label=_("&Rate")) 
     241                self.rateSlider=wx.Slider(self,wx.ID_ANY,minValue=0,maxValue=100,name="Rate:") 
     242                self.rateSlider.Bind(wx.EVT_SLIDER,self.onRateChange) 
     243                self._setSliderStepSizes(self.rateSlider,getSynth().rateMinStep) 
     244                sizer.Add(label) 
     245                sizer.Add(self.rateSlider) 
     246                settingsSizer.Add(sizer,border=10,flag=wx.BOTTOM) 
     247                sizer=wx.BoxSizer(wx.HORIZONTAL) 
     248                label=wx.StaticText(self,wx.ID_ANY,label=_("&Pitch")) 
     249                self.pitchSlider=wx.Slider(self,wx.ID_ANY,minValue=0,maxValue=100) 
     250                self._setSliderStepSizes(self.pitchSlider,getSynth().pitchMinStep) 
     251                self.pitchSlider.Bind(wx.EVT_SLIDER,self.onPitchChange) 
     252                sizer.Add(label) 
     253                sizer.Add(self.pitchSlider) 
     254                settingsSizer.Add(sizer,border=10,flag=wx.BOTTOM) 
     255                sizer=wx.BoxSizer(wx.HORIZONTAL) 
     256                label=wx.StaticText(self,wx.ID_ANY,label=_("&Inflection")) 
     257                self.inflectionSlider=wx.Slider(self,wx.ID_ANY,minValue=0,maxValue=100) 
     258                self._setSliderStepSizes(self.inflectionSlider,getSynth().inflectionMinStep) 
     259                self.inflectionSlider.Bind(wx.EVT_SLIDER,self.onInflectionChange) 
     260                sizer.Add(label) 
     261                sizer.Add(self.inflectionSlider) 
     262                settingsSizer.Add(sizer,border=10,flag=wx.BOTTOM) 
     263                sizer=wx.BoxSizer(wx.HORIZONTAL) 
     264                label=wx.StaticText(self,wx.ID_ANY,label=_("V&olume")) 
     265                self.volumeSlider=wx.Slider(self,wx.ID_ANY,minValue=0,maxValue=100) 
     266                self._setSliderStepSizes(self.volumeSlider,getSynth().volumeMinStep) 
     267                self.volumeSlider.Bind(wx.EVT_SLIDER,self.onVolumeChange) 
     268                sizer.Add(label) 
     269                sizer.Add(self.volumeSlider) 
     270                settingsSizer.Add(sizer,border=10,flag=wx.BOTTOM) 
     271                self._setVoiceParameters() 
    279272                self.punctuationCheckBox=wx.CheckBox(self,wx.NewId(),label=_("&Speak all punctuation")) 
    280273                self.punctuationCheckBox.SetValue(config.conf["speech"]["speakPunctuation"]) 
     
    293286                self.voiceList.SetFocus() 
    294287 
     288        def _setVoiceParameters(self): 
     289                if getSynth().hasRate: 
     290                        self.rateSlider.Enable() 
     291                        self.rateSlider.SetValue(getSynth().rate) 
     292                else: 
     293                        self.rateSlider.Disable() 
     294                if getSynth().hasPitch: 
     295                        self.pitchSlider.Enable() 
     296                        self.pitchSlider.SetValue(getSynth().pitch) 
     297                else: 
     298                        self.pitchSlider.Disable() 
     299                if getSynth().hasInflection: 
     300                        self.inflectionSlider.Enable() 
     301                        self.inflectionSlider.SetValue(getSynth().inflection) 
     302                else: 
     303                        self.inflectionSlider.Disable() 
     304                if getSynth().hasVolume: 
     305                        self.volumeSlider.Enable() 
     306                        self.volumeSlider.SetValue(getSynth().volume) 
     307                else: 
     308                        self.volumeSlider.Disable() 
     309 
    295310        def onVoiceChange(self,evt): 
    296                 changeVoice(getSynth(),evt.GetSelection()+1) 
    297                 rate=getSynth().rate 
    298                 self.rateSlider.SetValue(rate) 
    299                 pitch=getSynth().pitch 
    300                 self.pitchSlider.SetValue(pitch) 
    301                 volume=getSynth().volume 
    302                 self.volumeSlider.SetValue(volume) 
     311                changeVoice(getSynth(),self._voices[evt.GetSelection()].ID) 
     312                self._setVoiceParameters() 
    303313 
    304314        def onVariantChange(self,evt): 
    305                 val=evt.GetSelection() 
    306                 s=getSynth() 
    307                 s.variant=s.getVariantIdentifier(val) 
     315                getSynth().variant=self._variants[evt.GetSelection()].ID 
    308316 
    309317        def onRateChange(self,evt): 
     
    340348        def onOk(self,evt): 
    341349                if getSynth().hasVoice: 
    342                         config.conf["speech"][getSynth().name]["voice"]=self.voiceList.GetSelection()+1 
     350                        config.conf["speech"][getSynth().name]["voice"]=self._voices[self.voiceList.GetSelection()].ID 
    343351                if getSynth().hasVariant: 
    344                         config.conf["speech"][getSynth().name]["variant"]=getSynth().getVariantIdentifier(self.variantList.GetSelection()) 
     352                        config.conf["speech"][getSynth().name]["variant"]=self._variants[self.variantList.GetSelection()].ID 
    345353                if getSynth().hasRate: 
    346354                        config.conf["speech"][getSynth().name]["rate"]=self.rateSlider.GetValue() 
  • trunk/source/synthDriverHandler.py

    r2471 r2482  
    1919 
    2020def changeVoice(synth, voice): 
    21         voiceName=synth.getVoiceName(voice).replace('\\','_') 
     21        voiceName=synth.getVoiceInfoByID(voice).name 
     22        voiceName=voiceName.replace('\\','_') 
    2223        fileName="%s/%s-%s.dic"%(speechDictHandler.speechDictsPath,synth.name,voiceName) 
    2324        speechDictHandler.dictionaries["voice"].load(fileName) 
     
    5657                if not updatedConfig: 
    5758                        if newSynth.hasVoice: 
    58                                 changeVoice(newSynth,config.conf["speech"][name]["voice"]) 
     59                                voice=config.conf["speech"][name]["voice"] 
     60                                try: 
     61                                        changeVoice(newSynth,voice) 
     62                                except LookupError: 
     63                                        log.warning("No such voice: %s" % voice) 
    5964                        if newSynth.hasVariant: 
    6065                                newSynth.variant=config.conf["speech"][name]["variant"] 
     
    111116        for example, L{_get_pitch} and L{_set_pitch} for L{pitch}. 
    112117        The methods L{speakText}, L{cancel} and L{pause} should be overridden as appropriate. 
    113         @ivar voice: The current voice. 
    114         @type voice: int 
    115         @ivar voiceCount: The number of available voices. 
    116         @type voiceCount: int 
     118        @ivar voice: Unique string identifying the current voice. 
     119        @type voice: str 
     120        @ivar availableVoices: The available voices. 
     121        @ivar availableVoices: [L{VoiceInfo}, ...] 
    117122        @ivar pitch: The current pitch; ranges between 0 and 100. 
    118123        @type pitch: int 
     
    123128        @ivar variant: The current variant of the voice. 
    124129        @type variant: str 
    125         @ivar variantCount: The number of available variants. 
    126         @type variantCount: int 
     130        @ivar availableVariants: The available variants of the voice. 
     131        @type availableVariants: [L{VoiceInfo}, ...] 
    127132        @ivar inflection: The current inflection; ranges between 0 and 100. 
    128133        @type inflection: int 
     
    158163                return False 
    159164 
    160         def initialize(self): 
     165        def __init__(self): 
    161166                """Initialize this synth driver. 
    162167                This method can also set default settings for the synthesizer. 
     
    164169                @postcondition: This driver can be used. 
    165170                """ 
     171 
    166172 
    167173        def terminate(self): 
     
    195201 
    196202        def _get_voice(self): 
    197                 return 1 
     203                raise NotImplementedError 
    198204 
    199205        def _set_voice(self, value): 
    200206                pass 
    201207 
    202         def _get_voiceCount(self): 
    203                 return 1 
    204  
    205         def getVoiceName(self, num): 
    206                 return "" 
     208        def _getAvailableVoices(self): 
     209                """fetches a list of voices that the synth supports. 
     210                @returns: a list of L{VoiceInfo} instances representing the available voices 
     211                @rtype: list 
     212                """ 
     213 
     214        def _get_availableVoices(self): 
     215                if not hasattr(self,'_availableVoices'): 
     216                        self._availableVoices=self._getAvailableVoices() 
     217                return self._availableVoices 
    207218 
    208219        def _get_rate(self): 
     
    225236 
    226237        def _get_variant(self): 
    227                 return "none" 
     238                raise NotImplementedError 
    228239 
    229240        def _set_variant(self, value): 
    230241                pass 
    231242 
    232         def _get_variantCount(self): 
    233                 return 1 
     243        def _getAvailableVariants(self): 
     244                """fetches a list of variants that the synth supports. 
     245                @returns: a list of L{VoiceInfo} instances representing the available variants 
     246                @rtype: list 
     247                """ 
     248  
     249        def _get_availableVariants(self): 
     250                if not hasattr(self,'_availableVariants'): 
     251                        self._availableVariants=self._getAvailableVariants() 
     252                return self._availableVariants 
    234253 
    235254        def _get_inflection(self): 
     
    269288                """ 
    270289                return int(round(float(percent) / 100 * (max - min) + min)) 
     290 
     291        def getVoiceInfoByID(self,ID): 
     292                """Looks up a L{VoiceInfo} instance representing a particular voice, by its ID. 
     293                @param ID: the ID of the voice 
     294                @type ID: string 
     295                @returns: the voice info instance 
     296                @rtype: L{VoiceInfo} 
     297                @raise LookupError: If there was no such voice. 
     298                """ 
     299                for v in self.availableVoices: 
     300                        if v.ID==ID: 
     301                                return v 
     302                raise LookupError("No such voice") 
     303 
     304class VoiceInfo(object): 
     305        """Provides information about a single synthesizer voice. 
     306        """ 
     307 
     308        def __init__(self,ID,name): 
     309                #: The unique identifier of the voice. 
     310                #: @type: str 
     311                self.ID=ID 
     312                #: The name of the voice, visible to the user. 
     313                #: @type: str 
     314                self.name=name 
  • trunk/source/synthDrivers/_espeak.py

    r2348 r2482  
    143143        def run(self): 
    144144                global isSpeaking 
    145                 try: 
    146                         while True: 
    147                                 func, args, kwargs = bgQueue.get() 
    148                                 if not func: 
    149                                         break 
     145                while True: 
     146                        func, args, kwargs = bgQueue.get() 
     147                        if not func: 
     148                                break 
     149                        try: 
    150150                                func(*args, **kwargs) 
    151                                 bgQueue.task_done() 
    152                 except: 
    153                         log.error("bgThread.run", exc_info=True) 
     151                        except: 
     152                                log.error("Error running function from queue", exc_info=True) 
     153                        bgQueue.task_done() 
    154154 
    155155def _bgExec(func, *args, **kwargs): 
  • trunk/source/synthDrivers/audiologic.py

    r2471 r2482  
    5555                _audiologic.TtsStop() 
    5656 
     57        def _getAvailableVoices(self): 
     58                return [SynthDriverHandler.VoiceInfo("", "Tts3")] 
     59 
    5760        def _get_voice(self): 
    58                 return 1 
    59  
    60         def _get_voiceCount(self): 
    61                 return 1 
    62  
    63         def getVoiceName(self,num): 
    64                 return "Tts3" 
     61                return "" 
    6562 
    6663        def _get_rate(self): 
  • trunk/source/synthDrivers/espeak.py

    r2471 r2482  
    3030                lang=languageHandler.getLanguage() 
    3131                _espeak.setVoiceByLanguage(lang) 
    32                 self._voiceList=_espeak.getVoiceList() 
    3332                self._variantDict=_espeak.getVariantDict() 
    3433                self.variant="max" 
     
    3635                self.pitch=40 
    3736                self.inflection=75 
    38  
    3937 
    4038        def speakText(self,text,index=None): 
     
    7775                _espeak.setParameter(_espeak.espeakVOLUME,volume,0) 
    7876 
     77        def _getAvailableVoices(self): 
     78                return [synthDriverHandler.VoiceInfo(voice.identifier, "%s (%s)" % (voice.name, voice.identifier)) for voice in _espeak.getVoiceList()] 
     79 
    7980        def _get_voice(self): 
    8081                curVoice = _espeak.getCurrentVoice() 
    8182                if not curVoice: 
    82                         return 0 
    83                 for index, voice in enumerate(self._voiceList): 
    84                         if voice.identifier.split('+')[0] == curVoice.identifier.split('+')[0]: 
    85                                 return index + 1 
    86                 return 0 
     83                        return "" 
     84                return curVoice.identifier.split('+')[0] 
    8785 
    88         def _set_voice(self, index): 
    89                 if index == 0: 
     86        def _set_voice(self, identifier): 
     87                if not identifier: 
    9088                        return 
    91                 _espeak.setVoiceAndVariant(voice=self._voiceList[index - 1].identifier) 
    92  
    93         def _get_voiceCount(self): 
    94                 return len(self._voiceList) 
    95  
    96         def getVoiceName(self,num): 
    97                 num=num-1 
    98                 return "%s (%s)"%(self._voiceList[num].name,self._voiceList[num].identifier) 
     89                _espeak.setVoiceAndVariant(voice=identifier) 
    9990 
    10091        def _get_lastIndex(self): 
     
    111102                _espeak.setVoiceAndVariant(variant=val) 
    112103 
    113         def _get_variantCount(self): 
    114                 return len(self._variantDict) 
    115  
    116         def getVariantName(self,num): 
    117                 return self._variantDict.values()[num] 
    118  
    119         def getVariantIdentifier(self,num): 
    120                 return self._variantDict.keys()[num] 
     104        def _getAvailableVariants(self): 
     105                return [synthDriverHandler.VoiceInfo(ID, name) for ID, name in self._variantDict.iteritems()] 
  • trunk/source/synthDrivers/sapi4.py

    r2472 r2482  
    6464                if len(self._enginesList)==0: 
    6565                        raise RuntimeError("No Sapi4 engines available") 
    66                 self.voice=1 
     66                self.voice=str(self._enginesList[0].gModeID) 
    6767 
    6868        def speakText(self,text,index=None): 
     69                flags=0 
    6970                if index is not None: 
    70                         text="\mrk=%d\\%s"%(index,text) 
    71                 self._ttsCentral.TextData(VOICECHARSET.CHARSET_TEXT, TTSDATAFLAG_TAGGED,TextSDATA(text),self._bufSink._com_pointers_[ITTSBufNotifySink._iid_],ITTSBufNotifySink._iid_) 
     71                        text="\mrk=%d\\%s"%(index,text.replace('\\','\\\\')) 
     72                        flags+=TTSDATAFLAG_TAGGED 
     73                self._ttsCentral.TextData(VOICECHARSET.CHARSET_TEXT, flags,TextSDATA(text),self._bufSink._com_pointers_[ITTSBufNotifySink._iid_],ITTSBufNotifySink._iid_) 
    7274 
    7375        def cancel(self): 
     
    8183 
    8284        def _set_voice(self,val): 
    83                 self._voice=val-1 
     85                try: 
     86                        val=GUID(val) 
     87                except: 
     88                        val=self._enginesList[0].gModeID 
     89                mode=None 
     90                for mode in self._enginesList: 
     91                        if mode.gModeID==val: 
     92                                break 
     93                if mode is None: 
     94                        raise ValueError("no such mode: %s"%val) 
     95                self._currentMode=mode 
    8496                self._ttsAudio=CoCreateInstance(CLSID_MMAudioDest,IAudioMultiMediaDevice) 
    8597                self._ttsAudio.DeviceNumSet(nvwave.outputDeviceNameToID(config.conf["speech"]["outputDevice"], True)) 
    8698                self._ttsCentral=POINTER(ITTSCentralW)() 
    87                 self._ttsEngines.Select(self._enginesList[self._voice].gModeID,byref(self._ttsCentral),self._ttsAudio) 
     99                self._ttsEngines.Select(self._currentMode.gModeID,byref(self._ttsCentral),self._ttsAudio) 
    88100                self._tt