Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pre_filter_speechSequence and post_filter_speechSequence extension point Actions #17429

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion projectDocs/dev/developerGuide/developerGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -1382,7 +1382,9 @@ For examples of how to define and use new extension points, please see the code
|`Action` |`speechCanceled` |Triggered when speech is canceled.|
|`Action` |`pre_speechCanceled` |Triggered before speech is canceled.|
|`Action` |`pre_speech` |Triggered before NVDA handles prepared speech.|
|`Filter` |`filter_speechSequence` |Allows components or add-ons to filter speech sequence before it passes to the synth driver.|
| ``Action`` | ``pre_filter_speechSequence`` | Notifies before speech sequence filters are processed. |
| ``Action`` | ``post_filter_speechSequence`` | Notifies after a speech sequence has optionally been filtered by NVDA components and/or add-ons. |
| ``Filter`` | ``filter_speechSequence`` | Allows components or add-ons to filter a speech sequence before it is passed to the Synth driver. |
Comment on lines +1385 to +1387
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these should be single backticks, not doubled, please fix the formatting

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
| ``Filter`` | ``filter_speechSequence`` | Allows components or add-ons to filter a speech sequence before it is passed to the Synth driver. |
| `Filter` | `filter_speechSequence` | Allows components or add-ons to filter a speech sequence before it is passed to the synth driver. |


### synthDriverHandler {#synthDriverHandlerExtPts}

Expand Down
22 changes: 22 additions & 0 deletions source/speech/extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,28 @@
@type priority: priorities.Spri
"""

pre_filter_speechSequence = Action()
"""
Notifies before a speech sequence is filtered.

:param value: speech sequence.
:type value: SpeechSequence
"""

post_filter_speechSequence = Action()
"""
Notifies when speech has been filtered and is ready to be passed onto the synthesizer.

:param value: a planned speech sequence.
:type value: SpeechSequence
What NVDA would speak with speech turned on and uninterrupted.
A speech sequence that has been generated somewhere in NVDA,
and is finished being processed or modified by external modules such as add-ons.
Prepared speech may be cancelled or processed further to prepare it for the synthesizer; but this is as close to what is actually going to be spoken as it is possible to get before the synthesizer receives it.
This extension point is useful for tracking prepared speech in NVDA.
i.e. for speech viewer, capturing speech history, or mirroring speech in braille.
"""

filter_speechSequence = Filter[SpeechSequence]()
"""
Filters speech sequence before it passes to synthDriver.
Expand Down
16 changes: 10 additions & 6 deletions source/speech/speech.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,14 @@
from textUtils import unicodeNormalize
from textUtils.uniscribe import splitAtCharacterBoundaries
from . import manager
from .extensions import speechCanceled, pre_speechCanceled, pre_speech
from .extensions import filter_speechSequence
from .extensions import (
filter_speechSequence,
post_filter_speechSequence,
pre_filter_speechSequence,
pre_speechCanceled,
pre_speech,
speechCanceled,
)
from .commands import (
# Commands that are used in this file.
BreakCommand,
Expand Down Expand Up @@ -1073,17 +1079,15 @@ def speak( # noqa: C901
@param symbolLevel: The symbol verbosity level; C{None} (default) to use the user's configuration.
@param priority: The speech priority.
"""
pre_filter_speechSequence.notify(sequence=speechSequence)
speechSequence = filter_speechSequence.apply(speechSequence)
logBadSequenceTypes(speechSequence)
# in case priority was explicitly passed in as None, set to default.
priority: Spri = Spri.NORMAL if priority is None else priority

if not speechSequence: # Pointless - nothing to speak
return
import speechViewer

if speechViewer.isActive:
speechViewer.appendSpeechSequence(speechSequence)
post_filter_speechSequence.notify(sequence=speechSequence)
pre_speech.notify(speechSequence=speechSequence, symbolLevel=symbolLevel, priority=priority)
Comment on lines +1090 to 1091
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since these are called at the same point in time, aren't they semantically the same?
I don't think we need post_filter_speechSequence any more, pre_speech is the same concept.

if _speechState.speechMode == SpeechMode.off:
return
Expand Down
3 changes: 3 additions & 0 deletions source/speechViewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import config
from logHandler import log
from speech import SpeechSequence
from speech.extensions import post_filter_speechSequence
from gui import blockAction
import gui.contextHelp
from utils.security import isLockScreenModeActive, post_sessionLockStateChanged
Expand Down Expand Up @@ -47,6 +48,7 @@ def __init__(self, onDestroyCallBack: Callable[[], None]):
style=wx.CAPTION | wx.CLOSE_BOX | wx.RESIZE_BORDER | wx.STAY_ON_TOP,
)
post_sessionLockStateChanged.register(self.onSessionLockStateChange)
post_filter_speechSequence.register(appendSpeechSequence)
self._isDestroyed = False
self.onDestroyCallBack = onDestroyCallBack
self.Bind(wx.EVT_CLOSE, self.onClose)
Expand Down Expand Up @@ -130,6 +132,7 @@ def onShouldShowOnStartupChanged(self, evt: wx.CommandEvent):
def onDestroy(self, evt: wx.Event):
self._isDestroyed = True
post_sessionLockStateChanged.unregister(self.onSessionLockStateChange)
post_filter_speechSequence.unregister(appendSpeechSequence)
log.debug("SpeechViewer destroyed")
self.onDestroyCallBack()
evt.Skip()
Expand Down
4 changes: 4 additions & 0 deletions user_docs/en/changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ Add-ons will need to be re-tested and have their manifest updated.
* Removed the requirement to indent function parameter lists by two tabs from NVDA's Coding Standards, to be compatible with modern automatic linting. (#17126, @XLTechie)
* Added the [VS Code workspace configuration for NVDA](https://nvaccess.org/nvaccess/vscode-nvda) as a git submodule. (#17003)
* In the `brailleTables` module, a `getDefaultTableForCurrentLang` function has been added (#17222, @nvdaes)
* Added the following extension points:
* ``speech.filter_speechSequence``. (#16191, @beqabeqa473)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this has already been added no? this PR doesn't add it.

* ``speech.pre_filter_speechSequence``. (#16213, @beqabeqa473)
* ``speech.post_filter_speechSequence``. (#16213, @beqabeqa473)

#### API Breaking Changes

Expand Down