8181}
8282RE_MATH_LANG = re .compile (r"""<math .*(xml:)?lang=["']([^'"]+)["'].*>""" )
8383
84+ # try to get around espeak bug where voice slows down (for other voices, just a waste of time)
85+ # we use a global that gets set at a time when the rate is probably good (SetMathML)
86+ _synthesizer_rate : int | None = None
87+
8488
8589def getLanguageToUse (mathMl : str = "" ) -> str :
8690 """Get the language specified in a math tag if the language pref is Auto, else the language preference."""
@@ -123,15 +127,12 @@ def ConvertSSMLTextForNVDA(text: str) -> list:
123127 nvdaLanguage = getCurrentLanguage ().replace ("_" , "-" )
124128 # log.info(f"mathCATLanguageSetting={mathCATLanguageSetting}, lang={language}, NVDA={nvdaLanguage}")
125129
126- synth = getSynth ()
127130 _monkeyPatchESpeak ()
128- wpm = synth ._percentToParam (synth .rate , 80 , 450 )
129- try :
130- if synth .rateBoost :
131- wpm *= 3 # a guess based on espeak -- not sure what oneCore does
132- except AttributeError :
133- pass # SAPI voices don't have 'rateBoost' attr
134-
131+
132+ synth = getSynth ()
133+ # I tried the engines on a 180 word excerpt. The speeds do not change linearly and differ a it between engines
134+ # At "50" espeak finished in 46 sec, sapi in 75 sec, and one core in 70; at '100' one core was much slower than the others
135+ wpm = 2 * getSynth ()._get_rate ()
135136 breakMulti = 180.0 / wpm
136137 supported_commands = synth .supportedCommands
137138 use_break = BreakCommand in supported_commands
@@ -224,13 +225,21 @@ def __init__(self, provider=None, mathMl: Optional[str] = None):
224225
225226 def reportFocus (self ):
226227 super (MathCATInteraction , self ).reportFocus ()
228+ # try to get around espeak bug where voice slows down
229+ if _synthesizer_rate and getSynth ().name == 'espeak' :
230+ getSynth ()._set_rate (_synthesizer_rate )
227231 try :
228232 text = libmathcat .DoNavigateCommand ("ZoomIn" )
229233 speech .speak (ConvertSSMLTextForNVDA (text ))
230234 except Exception as e :
231235 log .exception (e )
232236 # Translators: this message directs users to look in the log file
233237 speech .speakMessage (_ ("Error in starting navigation of math: see NVDA error log for details" ))
238+ finally :
239+ # try to get around espeak bug where voice slows down
240+ if _synthesizer_rate and getSynth ().name == 'espeak' :
241+ # log.info(f'reportFocus: reset to {_synthesizer_rate}')
242+ getSynth ()._set_rate (_synthesizer_rate )
234243
235244 def getBrailleRegions (self , review : bool = False ):
236245 # log.info("***MathCAT start getBrailleRegions")
@@ -282,8 +291,10 @@ def getScript(self, gesture: KeyboardInputGesture):
282291 return super ().getScript (gesture )
283292
284293 def script_navigate (self , gesture : KeyboardInputGesture ):
285- # log.info("***MathCAT script_navigate")
286294 try :
295+ # try to get around espeak bug where voice slows down
296+ if _synthesizer_rate and getSynth ().name == 'espeak' :
297+ getSynth ()._set_rate (_synthesizer_rate )
287298 if (gesture is not None ): # == None when initial focus -- handled in reportFocus()
288299 modNames = gesture .modifierNames
289300 text = libmathcat .DoNavigateKeyPress (
@@ -299,6 +310,11 @@ def script_navigate(self, gesture: KeyboardInputGesture):
299310 log .exception (e )
300311 # Translators: this message directs users to look in the log file
301312 speech .speakMessage (_ ("Error in navigating math: see NVDA error log for details" ))
313+ finally :
314+ # try to get around espeak bug where voice slows down
315+ if _synthesizer_rate and getSynth ().name == 'espeak' :
316+ # log.info(f'script_navigate: reset to {_synthesizer_rate}')
317+ getSynth ()._set_rate (_synthesizer_rate )
302318
303319 if not braille .handler .enabled :
304320 return
@@ -460,6 +476,14 @@ def __init__(self):
460476 speech .speakMessage (_ ("MathCAT initialization failed: see NVDA error log for details" ))
461477
462478 def getSpeechForMathMl (self , mathml : str ):
479+ global _synthesizer_rate
480+ synth = getSynth ()
481+ synthConfig = config .conf ["speech" ][synth .name ]
482+ if synth .name == 'espeak' :
483+ _synthesizer_rate = synthConfig ['rate' ]
484+ # log.info(f'_synthesizer_rate={_synthesizer_rate}, get_rate()={getSynth()._get_rate()}')
485+ getSynth ()._set_rate (_synthesizer_rate )
486+ # log.info(f'..............get_rate()={getSynth()._get_rate()}, name={synth.name}')
463487 try :
464488 # need to set Language before the MathML for DecimalSeparator canonicalization
465489 language = getLanguageToUse (mathml )
@@ -473,8 +497,6 @@ def getSpeechForMathMl(self, mathml: str):
473497 speech .speakMessage (_ ("Illegal MathML found: see NVDA error log for details" ))
474498 libmathcat .SetMathML ("<math></math>" ) # set it to something
475499 try :
476- synth = getSynth ()
477- synthConfig = config .conf ["speech" ][synth .name ]
478500 supported_commands = synth .supportedCommands
479501 # Set preferences for capital letters
480502 libmathcat .SetPreference (
@@ -502,6 +524,12 @@ def getSpeechForMathMl(self, mathml: str):
502524 # Translators: this message directs users to look in the log file
503525 speech .speakMessage (_ ("Error in speaking math: see NVDA error log for details" ))
504526 return ["" ]
527+ finally :
528+ # try to get around espeak bug where voice slows down
529+ if _synthesizer_rate and getSynth ().name == 'espeak' :
530+ # log.info(f'getSpeechForMathMl: reset to {_synthesizer_rate}')
531+ getSynth ()._set_rate (_synthesizer_rate )
532+
505533
506534 def _add_sounds (self ):
507535 try :
@@ -612,9 +640,7 @@ def patched_speak(self, speechSequence: SpeechSequence): # noqa: C901
612640 textList .append ("</prosody>" )
613641 text = "" .join (textList )
614642 # log.info(f"monkey-patched text={text}")
615- # Added saving old rate and then resetting to that -- work around for https://github.com/nvaccess/nvda/issues/15221
616- # I'm not clear why this works since _set_rate() is called before the speech is finished speaking
617- synth = getSynth ()
618- oldRate = synth ._get_rate ()
643+ oldRate : int = getSynth ()._get_rate ()
619644 _espeak .speak (text )
620- synth ._set_rate (oldRate )
645+ # try to get around espeak bug where voice slows down
646+ getSynth ()._set_rate (oldRate )
0 commit comments