Skip to content

Commit b35ebde

Browse files
committed
Updates to speech
1 parent f94c55f commit b35ebde

3 files changed

Lines changed: 51 additions & 7 deletions

File tree

source/speech/commands.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,24 @@ class EndUtteranceCommand(SpeechCommand):
202202
def __repr__(self):
203203
return "EndUtteranceCommand()"
204204

205+
206+
class SuppressUnicodeNormalizationCommand(SpeechCommand):
207+
"""Suppresses Unicode normalization at a point in a speech sequence.
208+
For any text after this, Unicode normalization will be suppressed.
209+
To restore normalization, provide an EndUtteranceCommand.
210+
"""
211+
state: bool
212+
213+
def __init__(self, state: bool = True):
214+
"""
215+
@param state: SUppress normalization if True, don't suppress when False
216+
"""
217+
self.state = state
218+
219+
def __repr__(self):
220+
return f"SuppressUnicodeNormalizationCommand({self.state!r})"
221+
222+
205223
class BaseProsodyCommand(SynthParamCommand):
206224
"""Base class for commands which change voice prosody; i.e. pitch, rate, etc.
207225
The change to the setting is specified using either an offset or a multiplier, but not both.

source/speech/manager.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
# Commands that are used in this file.
1515
EndUtteranceCommand,
1616
LangChangeCommand,
17+
SuppressUnicodeNormalizationCommand,
1718
SynthParamCommand,
1819
BaseCallbackCommand,
1920
ConfigProfileTriggerCommand,
@@ -363,6 +364,8 @@ def _processSpeechSequence(self, inSeq: SpeechSequence):
363364
continue
364365
if isinstance(command, SynthParamCommand):
365366
paramTracker.update(command)
367+
if isinstance(command, SuppressUnicodeNormalizationCommand):
368+
continue # Not handled by speech manager
366369
outSeq.append(command)
367370
# Add the last sequence and make sure the sequence ends the utterance.
368371
self._ensureEndUtterance(outSeq, outSeqs, paramsToReplay, paramTracker)

source/speech/speech.py

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
LangChangeCommand,
3838
BeepCommand,
3939
EndUtteranceCommand,
40+
SuppressUnicodeNormalizationCommand,
4041
CharacterModeCommand,
4142
)
4243
from .shortcutKeys import getKeyboardShortcutsSpeech
@@ -160,11 +161,16 @@ def isBlank(text):
160161
RE_CONVERT_WHITESPACE = re.compile("[\0\r\n]")
161162

162163

163-
def processText(locale: str, text: str, symbolLevel: characterProcessing.SymbolLevel) -> str:
164+
def processText(
165+
locale: str,
166+
text: str,
167+
symbolLevel: characterProcessing.SymbolLevel,
168+
normalize: bool = False
169+
) -> str:
164170
text = speechDictHandler.processText(text)
165171
text = characterProcessing.processSpeechSymbols(locale, text, symbolLevel)
166172
text = RE_CONVERT_WHITESPACE.sub(" ", text)
167-
if config.conf["speech"]["unicodeNormalization"]:
173+
if normalize:
168174
text = unicodeNormalize(text)
169175
return text.strip()
170176

@@ -508,19 +514,28 @@ def getSpellingSpeech(
508514
capPitchChange = synthConfig["capPitchChange"]
509515
else:
510516
capPitchChange = 0
517+
unicodeNormalization = (
518+
not useCharacterDescriptions
519+
and bool(config.conf["speech"]["unicodeNormalization"])
520+
)
511521
seq = _getSpellingSpeechWithoutCharMode(
512522
text,
513523
locale,
514524
useCharacterDescriptions,
515525
sayCapForCapitals=synthConfig["sayCapForCapitals"],
516526
capPitchChange=capPitchChange,
517527
beepForCapitals=synthConfig["beepForCapitals"],
518-
unicodeNormalization=bool(config.conf["speech"]["unicodeNormalization"]),
528+
unicodeNormalization=unicodeNormalization,
519529
reportNormalizedForCharacterNavigation=config.conf["speech"]["reportNormalizedForCharacterNavigation"],
520530
)
521531
if synthConfig["useSpellingFunctionality"]:
522532
seq = _getSpellingSpeechAddCharMode(seq)
533+
# This function applies Unicode normalization as appropriate.
534+
# Therefore, suppress the global normalization that might still occur
535+
# (i.e. when speak calls the processText function).
536+
yield SuppressUnicodeNormalizationCommand(True)
523537
yield from seq
538+
yield SuppressUnicodeNormalizationCommand(False)
524539

525540

526541
def getCharDescListFromText(text,locale):
@@ -1036,6 +1051,7 @@ def speak( # noqa: C901
10361051
curLanguage=defaultLanguage=getCurrentLanguage()
10371052
prevLanguage=None
10381053
defaultLanguageRoot=defaultLanguage.split('_')[0]
1054+
unicodeNormalization = initialUnicodeNormalization = config.conf["speech"]["unicodeNormalization"]
10391055
oldSpeechSequence=speechSequence
10401056
speechSequence=[]
10411057
for item in oldSpeechSequence:
@@ -1065,12 +1081,19 @@ def speak( # noqa: C901
10651081
inCharacterMode=False
10661082
for index in range(len(speechSequence)):
10671083
item=speechSequence[index]
1068-
if isinstance(item,CharacterModeCommand):
1084+
if isinstance(item, CharacterModeCommand):
10691085
inCharacterMode=item.state
1070-
if autoLanguageSwitching and isinstance(item,LangChangeCommand):
1086+
elif autoLanguageSwitching and isinstance(item, LangChangeCommand):
10711087
curLanguage=item.lang
1072-
if isinstance(item,str):
1073-
speechSequence[index]=processText(curLanguage,item,symbolLevel)
1088+
elif isinstance(item, SuppressUnicodeNormalizationCommand):
1089+
unicodeNormalization = initialUnicodeNormalization and item.state
1090+
elif isinstance(item,str):
1091+
speechSequence[index]=processText(
1092+
curLanguage,
1093+
item,
1094+
symbolLevel,
1095+
normalize=unicodeNormalization
1096+
)
10741097
if not inCharacterMode:
10751098
speechSequence[index]+=CHUNK_SEPARATOR
10761099
_manager.speak(speechSequence, priority)

0 commit comments

Comments
 (0)