-
Notifications
You must be signed in to change notification settings - Fork 119
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added change engine mic state and mode on caster startup for DNS (#815)
- Loading branch information
1 parent
f795116
commit bd1f07c
Showing
8 changed files
with
252 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
# TODO: Create and utilize base class. These classes should be initialized only once. | ||
# TODO: Add a function for end-user user to overload in EngineConfigEarly and EngineConfigLate | ||
|
||
class EngineConfigEarly(): | ||
""" | ||
Initializes engine specific customizations before Nexus initializes. | ||
Grammars are not loaded | ||
""" | ||
from castervoice.lib import settings | ||
from dragonfly import get_engine | ||
engine = get_engine().name # get_engine used as a workaround for running Natlink inprocess | ||
|
||
def __init__(self): | ||
self.set_cancel_word() | ||
|
||
def set_cancel_word(self): | ||
""" | ||
Defines SymbolSpecs cancel word as "escape" for windows speech recognition (WSR) | ||
""" | ||
if self.engine in ["sapi5shared", "sapi5", "sapi5inproc"]: | ||
self.settings.WSR = True | ||
from castervoice.rules.ccr.standard import SymbolSpecs | ||
SymbolSpecs.set_cancel_word("escape") | ||
|
||
|
||
class EngineConfigLate(): | ||
""" | ||
Initializes engine specific customizations after Nexus has initialized. | ||
Grammars are loaded into engine. | ||
""" | ||
from castervoice.lib import settings | ||
from castervoice.lib import printer | ||
from dragonfly import get_current_engine | ||
engine = get_current_engine().name | ||
|
||
def __init__(self): | ||
from castervoice.lib import control # Access to Nexus instance | ||
self.instannce = control.nexus()._engine_modes_manager | ||
if self.engine != "text": | ||
self.set_default_mic_mode() | ||
self.set_engine_default_mode() | ||
|
||
|
||
def set_default_mic_mode(self): | ||
""" | ||
Sets the microphone state on Caster startup. | ||
""" | ||
# Only DNS supports mic_state 'off'. Substituts `sleep` mode on other engines" | ||
if self.settings.SETTINGS["engine"]["default_mic"]: # Default is `False` | ||
default_mic_state = self.settings.SETTINGS["engine"]["mic_mode"] # Default is `on` | ||
if self.engine != "natlink" and default_mic_state == "off": | ||
default_mic_state == "sleep" | ||
self.instannce.set_mic_mode(default_mic_state) | ||
|
||
|
||
def set_engine_default_mode(self): | ||
""" | ||
Sets the engine mode on Caster startup. | ||
""" | ||
# Only DNS supports 'normal'. Substituts `command` mode on other engines" | ||
if self.settings.SETTINGS["engine"]["default_engine_mode"]: # Default is `False` | ||
default_mode = self.settings.SETTINGS["engine"]["engine_mode"] # Default is `normal` | ||
if self.engine != "natlink" and default_mode == "normal": | ||
default_mode == "command" | ||
self.instannce.set_engine_mode(mode=default_mode, state=True) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
from dragonfly import get_engine, get_current_engine | ||
from castervoice.lib import settings, printer | ||
|
||
# TODO: Implement a grammar exclusivity for non-DNS engines in a separate class | ||
|
||
class EngineModesManager(object): | ||
""" | ||
Manages engine modes and microphone states using backend engine API and through dragonfly grammar exclusivity. | ||
""" | ||
engine = get_current_engine().name | ||
if engine == 'natlink': | ||
import natlink | ||
|
||
engine_modes = {"normal":0, "command":2, "dictation":1,"numbers":3, "spell":4} | ||
mic_modes = ["on", "sleeping", "off"] | ||
engine_state = None | ||
previous_engine_state = None | ||
mic_state = None | ||
|
||
def initialize(self): | ||
# Remove "normal" and "off" from 'states' for non-DNS based engines. | ||
if self.engine != 'natlink': | ||
self.engine_modes.pop("normal", 0) | ||
self.mic_modes.remove("off") | ||
# Sets 1st index key ("normal" or "command") depending on engine type as default mode | ||
self.engine_state = self.previous_engine_state = next(iter(self.engine_modes.keys())) | ||
|
||
|
||
def set_mic_mode(self, mode): | ||
""" | ||
Changes the engine microphone mode | ||
'on': mic is on | ||
'sleeping': mic from the sleeping and can be woken up by command | ||
'off': mic off and cannot be turned back on by voice. (DNS Only) | ||
""" | ||
if mode in self.mic_modes: | ||
self.mic_state = mode | ||
if self.engine == 'natlink': | ||
self.natlink.setMicState(mode) | ||
# From here other engines use grammar exclusivity to re-create the sleep mode | ||
#if mode != "off": # off does not need grammar exclusivity | ||
#pass | ||
# TODO: Implement mic mode sleep mode using grammar exclusivity. This should override DNS is built in sleep grammar but kept in sync automatically with natlink.setmic_state | ||
else: | ||
printer.out("Caster: 'set_mic_mode' is not implemented for '{}'".format(self.engine)) | ||
else: | ||
printer.out("Caster: '{}' is not a valid. set_mic_state modes are: 'off' - DNS Only, 'on', 'sleeping'".format(mode)) | ||
|
||
|
||
def get_mic_mode(self): | ||
""" | ||
Returns mic state. | ||
mode: string | ||
""" | ||
return self.mic_state | ||
|
||
|
||
def set_engine_mode(self, mode=None, state=True): | ||
""" | ||
Sets the engine mode so that only certain types of commands/dictation are recognized. | ||
'state': Bool - enable/disable mode. | ||
'True': replaces current mode (Default) | ||
'False': restores previous mode | ||
'normal': dictation and command (Default: DNS only) | ||
'dictation': Dictation only | ||
'command': Commands only (Default: Other engines) | ||
'numbers': Numbers only | ||
'spell': Spelling only | ||
""" | ||
if state and mode is not None: | ||
# Track previous engine state | ||
# TODO: Timer to synchronize natlink.getMicState() with mengine_state in case of changed by end-user via DNS GUI. | ||
self.previous_engine_state = self.engine_state | ||
else: | ||
if not state: | ||
# Restore previous mode | ||
mode = self.previous_engine_state | ||
else: | ||
printer.out("Caster: set_engine_mode: 'State' cannot be 'True' with a undefined a 'mode'") | ||
|
||
if mode in self.engine_modes: | ||
if self.engine == 'natlink': | ||
try: | ||
self.natlink.execScript("SetRecognitionMode {}".format(self.engine_modes[mode])) # engine_modes[mode] is an integer | ||
self.engine_state = mode | ||
except Exception as e: | ||
printer.out("natlink.execScript failed \n {}".format(e)) | ||
else: | ||
# TODO: Implement mode exclusivity. This should override DNS is built in sleep grammar but kept in sync automatically with natlinks SetRecognitionMode | ||
# Once DNS enters its native mode exclusivity will override the any native DNS mode except for normal/command mode. | ||
if self.engine == 'text': | ||
self.engine_state = mode | ||
else: | ||
printer.out("Caster: 'set_engine_mode' is not implemented for '{}'".format(self.engine)) | ||
else: | ||
printer.out("Caster: '{}' mode is not a valid. set_engine_mode: Modes: 'normal'- DNS Only, 'dictation', 'command', 'numbers', 'spell'".format(mode)) | ||
|
||
|
||
def get_engine_mode(self): | ||
""" | ||
Returns engine mode. | ||
mode: str | ||
""" | ||
return self.engine_state |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
from unittest import TestCase | ||
|
||
from castervoice.lib.ctrl.mgr.engine_manager import EngineModesManager | ||
|
||
class TestEngineModesManager(TestCase): | ||
_Manager = EngineModesManager() | ||
EngineModesManager().initialize() | ||
|
||
def test_set_engine_mode(self): | ||
self._Manager.set_engine_mode(mode="numbers", state=True) | ||
self.assertEqual("numbers", self._Manager.get_engine_mode()) | ||
|
||
def test_get_previous_engine_state(self): | ||
self._Manager.set_engine_mode(mode="spell", state=True) | ||
self._Manager.set_engine_mode(mode="dictation", state=True) | ||
self.assertEqual("spell", self._Manager.previous_engine_state) | ||
|
||
def test_restore_previous_engine_mode(self): | ||
self._Manager.set_engine_mode(mode="spell", state=True) | ||
self._Manager.set_engine_mode(mode="dictation", state=True) | ||
self._Manager.set_engine_mode(state=False) | ||
self.assertEqual("spell", self._Manager.get_engine_mode()) | ||
|
||
def test_fail_engine_mode_change(self): | ||
self._Manager.set_engine_mode(mode="numbers", state=True) | ||
self._Manager.set_engine_mode(state=True) | ||
self.assertEqual("numbers", self._Manager.get_engine_mode()) | ||
|
||
def test_fail_invalid_mode(self): | ||
self._Manager.set_engine_mode(mode="invalid", state=True) | ||
self.assertNotEqual("invalid", self._Manager.get_engine_mode()) | ||
|
||
def test_set_mic_mode(self): | ||
self._Manager.set_mic_mode("sleeping") | ||
self.assertEqual("sleeping", self._Manager.get_mic_mode()) |