Skip to content

Commit

Permalink
Deprecate import of Commands directly from speech module in favour of…
Browse files Browse the repository at this point in the history
… speech.commands (#12126)

* Include deprecations of import commands directly from speech

* fix flake8 issues

* changes to match reproduction steps

* update changelog
  • Loading branch information
seanbudd authored Mar 12, 2021
1 parent a6ba80c commit c901b0f
Show file tree
Hide file tree
Showing 17 changed files with 188 additions and 162 deletions.
2 changes: 1 addition & 1 deletion source/mathPres/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def getSpeechForMathMl(self, mathMl):
@param mathMl: The MathML markup.
@type mathMl: str
@return: A speech sequence.
@rtype: List[str, speech.SpeechCommand]
@rtype: List[str, SpeechCommand]
"""
raise NotImplementedError

Expand Down
30 changes: 19 additions & 11 deletions source/mathPres/mathPlayer.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@
import braille
import mathPres

from speech.commands import (
PitchCommand,
VolumeCommand,
RateCommand,
LangChangeCommand,
BreakCommand,
CharacterModeCommand,
PhonemeCommand,
)

RE_MP_SPEECH = re.compile(
# Break.
r"<break time='(?P<break>\d+)ms'/> ?"
Expand All @@ -35,9 +45,9 @@
# Actual content.
r"|(?P<content>[^<,]+)")
PROSODY_COMMANDS = {
"pitch": speech.PitchCommand,
"volume": speech.VolumeCommand,
"rate": speech.RateCommand,
"pitch": PitchCommand,
"volume": VolumeCommand,
"rate": RateCommand,
}
def _processMpSpeech(text, language):
# MathPlayer's default rate is 180 wpm.
Expand All @@ -47,16 +57,15 @@ def _processMpSpeech(text, language):
breakMulti = 180.0 / wpm
out = []
if language:
out.append(speech.LangChangeCommand(language))
out.append(LangChangeCommand(language))
resetProsody = set()
for m in RE_MP_SPEECH.finditer(text):
if m.lastgroup == "break":
out.append(speech.BreakCommand(time=int(m.group("break")) * breakMulti))
out.append(BreakCommand(time=int(m.group("break")) * breakMulti))
elif m.lastgroup == "char":
out.extend((speech.CharacterModeCommand(True),
m.group("char"), speech.CharacterModeCommand(False)))
out.extend((CharacterModeCommand(True), m.group("char"), CharacterModeCommand(False)))
elif m.lastgroup == "comma":
out.append(speech.BreakCommand(time=100))
out.append(BreakCommand(time=100))
elif m.lastgroup in PROSODY_COMMANDS:
command = PROSODY_COMMANDS[m.lastgroup]
out.append(command(multiplier=int(m.group(m.lastgroup)) / 100.0))
Expand All @@ -66,12 +75,11 @@ def _processMpSpeech(text, language):
out.append(command(multiplier=1))
resetProsody.clear()
elif m.lastgroup == "phonemeText":
out.append(speech.PhonemeCommand(m.group("ipa"),
text=m.group("phonemeText")))
out.append(PhonemeCommand(m.group("ipa"), text=m.group("phonemeText")))
elif m.lastgroup == "content":
out.append(m.group(0))
if language:
out.append(speech.LangChangeCommand(None))
out.append(LangChangeCommand(None))
return out

class MathPlayerInteraction(mathPres.MathInteractionNVDAObject):
Expand Down
16 changes: 9 additions & 7 deletions source/sayAllHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import queueHandler
import winKernel

from speech.commands import CallbackCommand, EndUtteranceCommand

CURSOR_CARET = 0
CURSOR_REVIEW = 1

Expand Down Expand Up @@ -69,7 +71,7 @@ def next(self):
if not obj:
return
# Call this method again when we start speaking this object.
callbackCommand = speech.CallbackCommand(self.next, name="say-all:next")
callbackCommand = CallbackCommand(self.next, name="say-all:next")
speech.speakObject(obj, reason=controlTypes.OutputReason.SAYALL, _prefixSpeechCommand=callbackCommand)

def stop(self):
Expand Down Expand Up @@ -148,8 +150,8 @@ def nextLine(self):
# No more text.
if isinstance(self.reader.obj, textInfos.DocumentWithPageTurns):
# Once the last line finishes reading, try turning the page.
cb = speech.CallbackCommand(self.turnPage, name="say-all:turnPage")
speech.speakWithoutPauses([cb, speech.EndUtteranceCommand()])
cb = CallbackCommand(self.turnPage, name="say-all:turnPage")
speech.speakWithoutPauses([cb, EndUtteranceCommand()])
else:
self.finish()
return
Expand All @@ -163,7 +165,7 @@ def nextLine(self):
def _onLineReached(obj=self.reader.obj, state=state):
self.lineReached(obj, bookmark, state)

cb = speech.CallbackCommand(
cb = CallbackCommand(
_onLineReached,
name="say-all:lineReached"
)
Expand Down Expand Up @@ -239,11 +241,11 @@ def finish(self):
# Otherwise, if a different synth is being used for say all,
# we might switch synths too early and truncate the final speech.
# We do this by putting a CallbackCommand at the start of a new utterance.
cb = speech.CallbackCommand(self.stop, name="say-all:stop")
cb = CallbackCommand(self.stop, name="say-all:stop")
speech.speakWithoutPauses([
speech.EndUtteranceCommand(),
EndUtteranceCommand(),
cb,
speech.EndUtteranceCommand()
EndUtteranceCommand()
])

def stop(self):
Expand Down
20 changes: 1 addition & 19 deletions source/speech/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,25 +34,7 @@
EndUtteranceCommand,
CharacterModeCommand,
)
from .commands import ( # noqa: F401
# F401 imported but unused:
# The following are imported here because other files that speech.py
# previously relied on "import * from .commands"
# New commands added to commands.py should be directly imported only where needed.
# Usage of these imports is deprecated and will be removed in 2021.1
SynthCommand,
IndexCommand,
SynthParamCommand,
BreakCommand,
BaseProsodyCommand,
VolumeCommand,
RateCommand,
PhonemeCommand,
BaseCallbackCommand,
CallbackCommand,
WaveFileCommand,
ConfigProfileTriggerCommand,
)

from . import types
from .types import (
SpeechSequence,
Expand Down
20 changes: 1 addition & 19 deletions source/speech/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,7 @@
IndexCommand,
_CancellableSpeechCommand,
)
from .commands import ( # noqa: F401
# F401 imported but unused:
# These are imported explicitly to maintain backwards compatibility and will be removed in
# 2021.1. Rather than rely on these imports, import directly from the commands module.
# New commands added to commands.py should be directly imported only where needed.
SpeechCommand,
PitchCommand,
LangChangeCommand,
BeepCommand,
CharacterModeCommand,
SynthCommand,
BreakCommand,
BaseProsodyCommand,
VolumeCommand,
RateCommand,
PhonemeCommand,
CallbackCommand,
WaveFileCommand,
)

from .priorities import Spri, SPEECH_PRIORITIES
from logHandler import log
from synthDriverHandler import getSynth
Expand Down
5 changes: 3 additions & 2 deletions source/speechXml.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from collections import namedtuple, OrderedDict
import re
import speech
from speech.commands import SpeechCommand
from logHandler import log

XML_ESCAPES = {
Expand Down Expand Up @@ -196,7 +197,7 @@ class SpeechXmlConverter(object):
Subclasses implement specific XML schemas by implementing methods which convert each speech command.
The method for a speech command should be named with the prefix "convert" followed by the command's class name.
For example, the handler for C{IndexCommand} should be named C{convertIndexCommand}.
These methods receive the L{speech.SpeechCommand} instance as their only argument.
These methods receive the L{SpeechCommand} instance as their only argument.
They should return an appropriate XmlBalancer command.
Subclasses may wish to extend L{generateBalancerCommands}
to produce additional XmlBalancer commands at the start or end;
Expand All @@ -210,7 +211,7 @@ def generateBalancerCommands(self, speechSequence):
for item in speechSequence:
if isinstance(item, str):
yield item
elif isinstance(item, speech.SpeechCommand):
elif isinstance(item, SpeechCommand):
name = type(item).__name__
# For example: self.convertIndexCommand
func = getattr(self, "convert%s" % name, None)
Expand Down
4 changes: 2 additions & 2 deletions source/synthDriverHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class SynthDriver(driverHandler.Driver):
e.g. the L{voice} attribute is used for the L{voice} setting.
These will usually be properties.
L{supportedCommands} should specify what synth commands the synthesizer supports.
At a minimum, L{speech.IndexCommand} must be supported.
At a minimum, L{IndexCommand} must be supported.
L{PitchCommand} must also be supported if you want capital pitch change to work;
support for the pitch setting is not sufficient.
L{supportedNotifications} should specify what notifications the synthesizer provides.
Expand Down Expand Up @@ -93,7 +93,7 @@ class SynthDriver(driverHandler.Driver):
#: @type: str
description = ""
#: The speech commands supported by the synth.
#: @type: set of L{speech.SynthCommand} subclasses.
#: @type: set of L{SynthCommand} subclasses.
supportedCommands = frozenset()
#: The notifications provided by the synth.
#: @type: set of L{extensionPoints.Action} instances
Expand Down
43 changes: 27 additions & 16 deletions source/synthDrivers/espeak.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@
from logHandler import log
from driverHandler import BooleanDriverSetting

from speech.commands import (
IndexCommand,
CharacterModeCommand,
LangChangeCommand,
BreakCommand,
PitchCommand,
RateCommand,
VolumeCommand,
PhonemeCommand,
)

class SynthDriver(SynthDriver):
name = "espeak"
description = "eSpeak NG"
Expand All @@ -29,14 +40,14 @@ class SynthDriver(SynthDriver):
SynthDriver.VolumeSetting(),
)
supportedCommands = {
speech.IndexCommand,
speech.CharacterModeCommand,
speech.LangChangeCommand,
speech.BreakCommand,
speech.PitchCommand,
speech.RateCommand,
speech.VolumeCommand,
speech.PhonemeCommand,
IndexCommand,
CharacterModeCommand,
LangChangeCommand,
BreakCommand,
PitchCommand,
RateCommand,
VolumeCommand,
PhonemeCommand,
}
supportedNotifications = {synthIndexReached, synthDoneSpeaking}

Expand All @@ -60,9 +71,9 @@ def _get_language(self):
return self._language

PROSODY_ATTRS = {
speech.PitchCommand: "pitch",
speech.VolumeCommand: "volume",
speech.RateCommand: "rate",
PitchCommand: "pitch",
VolumeCommand: "volume",
RateCommand: "rate",
}

IPA_TO_ESPEAK = {
Expand Down Expand Up @@ -91,16 +102,16 @@ def speak(self,speechSequence):
for item in speechSequence:
if isinstance(item,str):
textList.append(self._processText(item))
elif isinstance(item,speech.IndexCommand):
elif isinstance(item, IndexCommand):
textList.append("<mark name=\"%d\" />"%item.index)
elif isinstance(item,speech.CharacterModeCommand):
elif isinstance(item, CharacterModeCommand):
textList.append("<say-as interpret-as=\"characters\">" if item.state else "</say-as>")
elif isinstance(item,speech.LangChangeCommand):
elif isinstance(item, LangChangeCommand):
if langChanged:
textList.append("</voice>")
textList.append("<voice xml:lang=\"%s\">"%(item.lang if item.lang else defaultLanguage).replace('_','-'))
langChanged=True
elif isinstance(item,speech.BreakCommand):
elif isinstance(item, BreakCommand):
textList.append('<break time="%dms" />' % item.time)
elif type(item) in self.PROSODY_ATTRS:
if prosody:
Expand All @@ -121,7 +132,7 @@ def speak(self,speechSequence):
for attr,val in prosody.items():
textList.append(' %s="%d%%"'%(attr,val))
textList.append(">")
elif isinstance(item,speech.PhonemeCommand):
elif isinstance(item, PhonemeCommand):
# We can't use str.translate because we want to reject unknown characters.
try:
phonemes="".join([self.IPA_TO_ESPEAK[char] for char in item.ipa])
Expand Down
33 changes: 22 additions & 11 deletions source/synthDrivers/oneCore.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@
import winVersion
import NVDAHelper

from speech.commands import (
IndexCommand,
CharacterModeCommand,
LangChangeCommand,
BreakCommand,
PitchCommand,
RateCommand,
VolumeCommand,
PhonemeCommand,
)

#: The number of 100-nanosecond units in 1 second.
HUNDRED_NS_PER_SEC = 10000000 # 1000000000 ns per sec / 100 ns
ocSpeech_Callback = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_int, ctypes.c_wchar_p)
Expand Down Expand Up @@ -78,9 +89,9 @@ def generateBalancerCommands(self, speechSequence):
yield next(commands)
# OneCore didn't provide a way to set base prosody values before API version 5.
# Therefore, the base values need to be set using SSML.
yield self.convertRateCommand(speech.RateCommand(multiplier=1))
yield self.convertVolumeCommand(speech.VolumeCommand(multiplier=1))
yield self.convertPitchCommand(speech.PitchCommand(multiplier=1))
yield self.convertRateCommand(RateCommand(multiplier=1))
yield self.convertVolumeCommand(VolumeCommand(multiplier=1))
yield self.convertPitchCommand(PitchCommand(multiplier=1))
for command in commands:
yield command

Expand All @@ -105,14 +116,14 @@ class SynthDriver(SynthDriver):
# Translators: Description for a speech synthesizer.
description = _("Windows OneCore voices")
supportedCommands = {
speech.IndexCommand,
speech.CharacterModeCommand,
speech.LangChangeCommand,
speech.BreakCommand,
speech.PitchCommand,
speech.RateCommand,
speech.VolumeCommand,
speech.PhonemeCommand,
IndexCommand,
CharacterModeCommand,
LangChangeCommand,
BreakCommand,
PitchCommand,
RateCommand,
VolumeCommand,
PhonemeCommand,
}
supportedNotifications = {synthIndexReached, synthDoneSpeaking}

Expand Down
9 changes: 5 additions & 4 deletions source/synthDrivers/sapi4.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import nvwave
import weakref

from speech.commands import IndexCommand, SpeechCommand, CharacterModeCommand

class SynthDriverBufSink(COMObject):
_com_interfaces_ = [ITTSBufNotifySink]
Expand Down Expand Up @@ -96,16 +97,16 @@ def speak(self,speechSequence):
for item in speechSequence:
if isinstance(item,str):
textList.append(item.replace('\\','\\\\'))
elif isinstance(item,speech.IndexCommand):
elif isinstance(item, IndexCommand):
textList.append("\\mrk=%d\\"%item.index)
elif isinstance(item,speech.CharacterModeCommand):
elif isinstance(item, CharacterModeCommand):
textList.append("\\RmS=1\\" if item.state else "\\RmS=0\\")
charMode=item.state
elif isinstance(item,speech.SpeechCommand):
elif isinstance(item, SpeechCommand):
log.debugWarning("Unsupported speech command: %s"%item)
else:
log.error("Unknown speech: %s"%item)
if isinstance(item,speech.IndexCommand):
if isinstance(item, IndexCommand):
# This is the index denoting the end of the speech sequence.
self._finalIndex=item.index
if charMode:
Expand Down
Loading

0 comments on commit c901b0f

Please sign in to comment.