Skip to content

Commit

Permalink
Added the following extension points:
Browse files Browse the repository at this point in the history
* inputCore.manager.decide_executeGesture: Decider for filtering gestures
* tones.decide_beep
* nvwave.decide_playWaveFile
  • Loading branch information
Leonard de Ruijter committed Aug 15, 2018
1 parent 671b202 commit c76ce8e
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 11 deletions.
18 changes: 17 additions & 1 deletion source/inputCore.py
Expand Up @@ -30,6 +30,7 @@
import languageHandler
import controlTypes
import keyLabels
import extensionPoints

#: Script category for emulated keyboard keys.
# Translators: The name of a category of NVDA commands.
Expand Down Expand Up @@ -208,7 +209,7 @@ def clear(self):
def add(self, gesture, module, className, script,replace=False):
"""Add a gesture mapping.
@param gesture: The gesture identifier.
@type gesture: str
@type gesture: L{InputGesture}
@param module: The name of the Python module containing the target script.
@type module: str
@param className: The name of the class in L{module} containing the target script.
Expand Down Expand Up @@ -403,6 +404,15 @@ def __init__(self):
self.loadLocaleGestureMap()
self.loadUserGestureMap()

#: Notifies when a gesture is about to be executed,
#: and allows components or add-ons to decide whether or not to execute a gesture.
#: For example, when controlling a remote system with a connected local braille display,
#: braille display gestures should not be executed locally.
#: Handlers are called with one argument:
#: @param gesture: The gesture that is about to be executed.
#: @type gesture: L{InputGesture}
self.decide_ExecuteGesture = extensionPoints.Decider()

def executeGesture(self, gesture):
"""Perform the action associated with a gesture.
@param gesture: The gesture to execute.
Expand All @@ -415,6 +425,12 @@ def executeGesture(self, gesture):
# as well as stopping a flood of actions when the core revives.
raise NoInputGestureAction

if not self.decide_executeGesture.decide(gesture=gesture):
# A registered handler decided that this gesture shouldn't be executed.
# Purposely do not raise a NoInputGestureAction here, as that could lead to unexpected behavior for gesture emulation.
log.debug("Gesture execution canceled by handler registered to decide_executeGesture extension point")
return

script = gesture.script
focus = api.getFocusObject()
if focus.sleepMode is focus.SLEEP_FULL or (focus.sleepMode and not getattr(script, 'allowInSleepMode', False)):
Expand Down
26 changes: 23 additions & 3 deletions source/nvwave.py
@@ -1,6 +1,6 @@
#nvwave.py
#A part of NonVisual Desktop Access (NVDA)
#Copyright (C) 2007-2017 NV Access Limited, Aleksey Sadovoy
#Copyright (C) 2007-2017 NV Access Limited, Aleksey Sadovoy, Babbage B.V.
#This file is covered by the GNU General Public License.
#See the file COPYING for more details.

Expand All @@ -17,6 +17,7 @@
import wave
import config
from logHandler import log
import extensionPoints

__all__ = (
"WavePlayer", "getOutputDeviceNames", "outputDeviceIDToName", "outputDeviceNameToID",
Expand All @@ -27,6 +28,14 @@
HWAVEOUT = HANDLE
LPHWAVEOUT = POINTER(HWAVEOUT)

#: Notifies when a wave file is about to be played,
#: and allows components or add-ons to decide whether the wave file should be played.
#: For example, when controlling a remote system,
#: the remote system must be notified of sounds played on the local system.
#: Also, registrars should be able to suppress playing sounds if desired.
#: Handlers are called with the same arguments as L{playWaveFile} as keyword arguments.
decide_playWaveFile = extensionPoints.Decider()

class WAVEFORMATEX(Structure):
_fields_ = [
("wFormatTag", WORD),
Expand Down Expand Up @@ -329,16 +338,27 @@ def outputDeviceNameToID(name, useDefaultIfInvalid=False):

fileWavePlayer = None
fileWavePlayerThread=None
def playWaveFile(fileName, async=True):
def playWaveFile(fileName, async=True, partOfSpeechSequence=False):
"""plays a specified wave file.
"""
@param fileName: the path to the wave file, usually absolute.
@type fileName: basestring
@param async: whether the file should be played asynchronously.
If C{False}, the calling thread is blocked until the wave has finished playing.
@type async: bool
@param partOfSpeechSequence: whether this beep is created as part of a speech sequence.
@type partOfSpeechSequence: bool
"""
global fileWavePlayer, fileWavePlayerThread
f = wave.open(fileName,"r")
if f is None: raise RuntimeError("can not open file %s"%fileName)
if fileWavePlayer is not None:
fileWavePlayer.stop()
if not decide_playWaveFile.decide(fileName=fileName, async=async, partOfSpeechSequence=partOfSpeechSequence):
log.debug("Playing wave file canceled by handler registered to decide_playWaveFile extension point")
return
fileWavePlayer = WavePlayer(channels=f.getnchannels(), samplesPerSec=f.getframerate(),bitsPerSample=f.getsampwidth()*8, outputDevice=config.conf["speech"]["outputDevice"],wantDucking=False)
fileWavePlayer.feed(f.readframes(f.getnframes()))

if async:
if fileWavePlayerThread is not None:
fileWavePlayerThread.join()
Expand Down
28 changes: 21 additions & 7 deletions source/tones.py
@@ -1,6 +1,6 @@
#tones.py
#A part of NonVisual Desktop Access (NVDA)
#Copyright (C) 2007-2017 NV Access Limited, Aleksey Sadovoy
#Copyright (C) 2007-2017 NV Access Limited, Aleksey Sadovoy, Babbage B.V.
#This file is covered by the GNU General Public License.
#See the file COPYING for more details.

Expand All @@ -12,13 +12,14 @@
import globalVars
from logHandler import log
from ctypes import create_string_buffer, byref
import extensionPoints

SAMPLE_RATE = 44100

try:
player = nvwave.WavePlayer(channels=2, samplesPerSec=int(SAMPLE_RATE), bitsPerSample=16, outputDevice=config.conf["speech"]["outputDevice"],wantDucking=False)
except:
log.warning("Failed to initialize audio for tones")
log.warning("Failed to initialize audio for tones", exc_info=True)
player = None

# When exiting, ensure player is deleted before modules get cleaned up.
Expand All @@ -28,18 +29,31 @@ def _cleanup():
global player
player = None

def beep(hz,length,left=50,right=50):
#: Notifies when a beep is about to be generated and played,
#: and allows components or add-ons to decide whether the beep should actually be played.
#: For example, when controlling a remote system,
#: the remote system must be notified of beeps played on the local system.
#: Also, registrars should be able to suppress playing beeps if desired.
#: Handlers are called with the same arguments as L{beep} as keyword arguments.
decide_beep = extensionPoints.Decider()

def beep(hz,length,left=50,right=50,partOfSpeechSequence=False):
"""Plays a tone at the given hz, length, and stereo balance.
@param hz: pitch in hz of the tone
@param hz: pitch in hz of the tone.
@type hz: float
@param length: length of the tone in ms
@param length: length of the tone in ms.
@type length: integer
@param left: volume of the left channel (0 to 100)
@param left: volume of the left channel (0 to 100).
@type left: integer
@param right: volume of the right channel (0 to 100)
@param right: volume of the right channel (0 to 100).
@type right: integer
@param partOfSpeechSequence: whether this beep is created as part of a speech sequence.
@type partOfSpeechSequence: bool
"""
log.io("Beep at pitch %s, for %s ms, left volume %s, right volume %s"%(hz,length,left,right))
if not decide_beep.decide(hz=hz, length=length, left=left, right=right, partOfSpeechSequence=partOfSpeechSequence):
log.debug("Beep canceled by handler registered to decide_beep extension point")
return
if not player:
return
from NVDAHelper import generateBeep
Expand Down

0 comments on commit c76ce8e

Please sign in to comment.