Browse Source

added the initial version for voice command spotting

master
rose-jinyang 3 years ago
parent
commit
8279d77e42
  1. 11
      .idea/LinVAM.iml
  2. 4
      .idea/misc.xml
  3. 8
      .idea/modules.xml
  4. 6
      .idea/vcs.xml
  5. 379
      .idea/workspace.xml
  6. 16
      README.md
  7. BIN
      __pycache__/commandeditwnd.cpython-36.pyc
  8. BIN
      __pycache__/keyactioneditwnd.cpython-36.pyc
  9. BIN
      __pycache__/mouseactioneditwnd.cpython-36.pyc
  10. BIN
      __pycache__/pauseactioneditwnd.cpython-36.pyc
  11. BIN
      __pycache__/profileeditwnd.cpython-36.pyc
  12. BIN
      __pycache__/profileexecutor.cpython-36.pyc
  13. BIN
      __pycache__/ui_commandeditwnd.cpython-36.pyc
  14. BIN
      __pycache__/ui_keyactioneditwnd.cpython-36.pyc
  15. BIN
      __pycache__/ui_mainwnd.cpython-36.pyc
  16. BIN
      __pycache__/ui_mouseactioneditwnd.cpython-36.pyc
  17. BIN
      __pycache__/ui_pauseactioneditwnd.cpython-36.pyc
  18. BIN
      __pycache__/ui_profileeditwnd.cpython-36.pyc
  19. 5
      command.list
  20. 2
      commandeditwnd.py
  21. 26
      commandeditwnd.ui
  22. 64
      main.py
  23. 134782
      model/en-us/cmudict-en-us.dict
  24. BIN
      model/en-us/en-us-phone.lm.bin
  25. BIN
      model/en-us/en-us.lm.bin
  26. 34
      model/en-us/en-us/README
  27. 12
      model/en-us/en-us/feat.params
  28. BIN
      model/en-us/en-us/mdef
  29. BIN
      model/en-us/en-us/means
  30. 5
      model/en-us/en-us/noisedict
  31. BIN
      model/en-us/en-us/sendump
  32. BIN
      model/en-us/en-us/transition_matrices
  33. BIN
      model/en-us/en-us/variances
  34. 107
      profileeditwnd.py
  35. 215
      profileexecutor.py
  36. BIN
      profiles.dat
  37. 301
      set_kws_threshold.py
  38. 31
      ui_commandeditwnd.py

11
.idea/LinVAM.iml

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="TestRunnerService">
<option name="PROJECT_TEST_RUNNER" value="Unittests" />
</component>
</module>

4
.idea/misc.xml

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/LinVAM.iml" filepath="$PROJECT_DIR$/.idea/LinVAM.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

379
.idea/workspace.xml

@ -0,0 +1,379 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="c0abc1c4-68ed-45ab-ade0-c412031fac32" name="Default Changelist" comment="">
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/commandeditwnd.py" beforeDir="false" afterPath="$PROJECT_DIR$/commandeditwnd.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/commandeditwnd.ui" beforeDir="false" afterPath="$PROJECT_DIR$/commandeditwnd.ui" afterDir="false" />
<change beforePath="$PROJECT_DIR$/main.py" beforeDir="false" afterPath="$PROJECT_DIR$/main.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/profileeditwnd.py" beforeDir="false" afterPath="$PROJECT_DIR$/profileeditwnd.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/profileexecutor.py" beforeDir="false" afterPath="$PROJECT_DIR$/profileexecutor.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/profiles.dat" beforeDir="false" afterPath="$PROJECT_DIR$/profiles.dat" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ui_commandeditwnd.py" beforeDir="false" afterPath="$PROJECT_DIR$/ui_commandeditwnd.py" afterDir="false" />
</list>
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileEditorManager">
<leaf SIDE_TABS_SIZE_LIMIT_KEY="450">
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/main.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="4900">
<caret line="207" column="10" selection-start-line="207" selection-start-column="10" selection-end-line="207" selection-end-column="10" />
<folding>
<element signature="e#0#26#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/kws.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="625">
<caret line="27" column="9" selection-start-line="27" selection-start-column="9" selection-end-line="27" selection-end-column="9" />
<folding>
<element signature="e#0#23#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/commandeditwnd.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="425">
<caret line="25" column="75" selection-start-line="25" selection-start-column="61" selection-end-line="25" selection-end-column="75" />
<folding>
<element signature="e#0#26#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/profileeditwnd.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="2200">
<caret line="95" column="31" selection-start-line="95" selection-start-column="31" selection-end-line="95" selection-end-column="31" />
<folding>
<element signature="e#0#26#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/ui_mouseactioneditwnd.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="2625">
<caret line="105" column="23" selection-start-line="105" selection-start-column="23" selection-end-line="105" selection-end-column="23" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/profileexecutor.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="244">
<caret line="64" column="50" lean-forward="true" selection-start-line="64" selection-start-column="50" selection-end-line="64" selection-end-column="50" />
<folding>
<element signature="e#0#15#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/set_kws_threshold.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="375">
<caret line="27" column="20" selection-start-line="27" selection-start-column="4" selection-end-line="27" selection-end-column="20" />
<folding>
<element signature="e#73#110#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/keyboard/__init__.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="21850">
<caret line="880" column="21" selection-start-line="880" selection-start-column="8" selection-end-line="880" selection-end-column="21" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$USER_HOME$/.PyCharmCE2019.1/system/python_stubs/-756103818/cv2/cv2/__init__.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="272375">
<caret line="10932" column="11" selection-start-line="10932" selection-start-column="11" selection-end-line="10932" selection-end-column="11" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/keyactioneditwnd.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="150">
<caret line="6" column="11" selection-start-line="6" selection-start-column="11" selection-end-line="6" selection-end-column="11" />
</state>
</provider>
</entry>
</file>
</leaf>
</component>
<component name="FindInProjectRecents">
<findStrings>
<find>termios</find>
<find>OUTPUT_FILENAME</find>
</findStrings>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="IdeDocumentHistory">
<option name="CHANGED_PATHS">
<list>
<option value="$PROJECT_DIR$/kws.py" />
<option value="$PROJECT_DIR$/commandeditwnd.py" />
<option value="$PROJECT_DIR$/profileexecutor.py" />
<option value="$PROJECT_DIR$/profileeditwnd.py" />
<option value="$PROJECT_DIR$/main.py" />
</list>
</option>
</component>
<component name="ProjectConfigurationFiles">
<option name="files">
<list>
<option value="$PROJECT_DIR$/.idea/LinVAM.iml" />
<option value="$PROJECT_DIR$/.idea/vcs.xml" />
<option value="$PROJECT_DIR$/.idea/misc.xml" />
<option value="$PROJECT_DIR$/.idea/modules.xml" />
</list>
</option>
</component>
<component name="ProjectFrameBounds" extendedState="6">
<option name="x" value="146" />
<option name="y" value="-8" />
<option name="width" value="1390" />
<option name="height" value="1000" />
</component>
<component name="ProjectView">
<navigator proportions="" version="1">
<foldersAlwaysOnTop value="true" />
</navigator>
<panes>
<pane id="ProjectPane">
<subPane>
<expand>
<path>
<item name="LinVAM" type="b2602c69:ProjectViewProjectNode" />
<item name="LinVAM" type="462c0819:PsiDirectoryNode" />
</path>
</expand>
<select />
</subPane>
</pane>
<pane id="Scope" />
</panes>
</component>
<component name="PropertiesComponent">
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
</component>
<component name="RunDashboard">
<option name="ruleStates">
<list>
<RuleState>
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
</RuleState>
<RuleState>
<option name="name" value="StatusDashboardGroupingRule" />
</RuleState>
</list>
</option>
</component>
<component name="RunManager">
<configuration name="main" type="PythonConfigurationType" factoryName="Python" temporary="true">
<module name="LinVAM" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/main.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<recent_temporary>
<list>
<item itemvalue="Python.main" />
</list>
</recent_temporary>
</component>
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="c0abc1c4-68ed-45ab-ade0-c412031fac32" name="Default Changelist" comment="" />
<created>1554735223942</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1554735223942</updated>
</task>
<servers />
</component>
<component name="ToolWindowManager">
<frame x="-8" y="-8" width="1936" height="1056" extended-state="6" />
<editor active="true" />
<layout>
<window_info content_ui="combo" id="Project" order="0" visible="true" weight="0.19989395" />
<window_info id="Structure" order="1" side_tool="true" weight="0.25" />
<window_info id="Favorites" order="2" side_tool="true" />
<window_info anchor="bottom" id="Message" order="0" />
<window_info anchor="bottom" id="Find" order="1" />
<window_info anchor="bottom" id="Run" order="2" weight="0.31687716" />
<window_info anchor="bottom" id="Debug" order="3" weight="0.39954075" />
<window_info anchor="bottom" id="Cvs" order="4" weight="0.25" />
<window_info anchor="bottom" id="Inspection" order="5" weight="0.4" />
<window_info anchor="bottom" id="TODO" order="6" />
<window_info anchor="bottom" id="Version Control" order="7" />
<window_info anchor="bottom" id="Terminal" order="8" weight="0.3295063" />
<window_info anchor="bottom" id="Event Log" order="9" side_tool="true" />
<window_info anchor="bottom" id="Python Console" order="10" weight="0.3295063" />
<window_info anchor="right" id="Commander" internal_type="SLIDING" order="0" type="SLIDING" weight="0.4" />
<window_info anchor="right" id="Ant Build" order="1" weight="0.25" />
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
</layout>
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/pauseactioneditwnd.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="300">
<caret line="12" column="24" selection-start-line="12" selection-start-column="24" selection-end-line="12" selection-end-column="24" />
<folding>
<element signature="e#0#26#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/ui_mainwnd.py">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$APPLICATION_HOME_DIR$/helpers/typeshed/stdlib/2and3/threading.pyi">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$USER_HOME$/AppData/Local/Programs/Python/Python36/Lib/threading.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="50">
<caret line="827" column="8" selection-start-line="827" selection-start-column="8" selection-end-line="827" selection-end-column="8" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/main.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="4900">
<caret line="207" column="10" selection-start-line="207" selection-start-column="10" selection-end-line="207" selection-end-column="10" />
<folding>
<element signature="e#0#26#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/kws.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="625">
<caret line="27" column="9" selection-start-line="27" selection-start-column="9" selection-end-line="27" selection-end-column="9" />
<folding>
<element signature="e#0#23#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/commandeditwnd.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="425">
<caret line="25" column="75" selection-start-line="25" selection-start-column="61" selection-end-line="25" selection-end-column="75" />
<folding>
<element signature="e#0#26#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/profileeditwnd.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="2200">
<caret line="95" column="31" selection-start-line="95" selection-start-column="31" selection-end-line="95" selection-end-column="31" />
<folding>
<element signature="e#0#26#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/ui_mouseactioneditwnd.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="2625">
<caret line="105" column="23" selection-start-line="105" selection-start-column="23" selection-end-line="105" selection-end-column="23" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/set_kws_threshold.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="375">
<caret line="27" column="20" selection-start-line="27" selection-start-column="4" selection-end-line="27" selection-end-column="20" />
<folding>
<element signature="e#73#110#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/keyboard/__init__.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="21850">
<caret line="880" column="21" selection-start-line="880" selection-start-column="8" selection-end-line="880" selection-end-column="21" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.PyCharmCE2019.1/system/python_stubs/-756103818/cv2/cv2/__init__.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="272375">
<caret line="10932" column="11" selection-start-line="10932" selection-start-column="11" selection-end-line="10932" selection-end-column="11" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/keyactioneditwnd.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="150">
<caret line="6" column="11" selection-start-line="6" selection-start-column="11" selection-end-line="6" selection-end-column="11" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/profileexecutor.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="244">
<caret line="64" column="50" lean-forward="true" selection-start-line="64" selection-start-column="50" selection-end-line="64" selection-end-column="50" />
<folding>
<element signature="e#0#15#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</component>
</project>

16
README.md

@ -1,2 +1,18 @@
# LinVAM
Linux Voice Activated Macro
## Status
This project is currently a work-in-progress and is minimally functional.
## Requirements
- python3
- PyQt5
- python3-xlib
## Install
- $ pip3 install PyQt5
- $ pip3 install python3-xlib
- $ git clone https://github.com/rose-jinyang/LinVAM.git
## Usage
This script must be run with root privilege because it must hook and simulate input devices such as keyboard, mouse etc.
- $ cd LinVAM
- $ xhost +
- $ sudo python3 ./main.py

BIN
__pycache__/commandeditwnd.cpython-36.pyc

diff.bin_not_shown

BIN
__pycache__/keyactioneditwnd.cpython-36.pyc

diff.bin_not_shown

BIN
__pycache__/mouseactioneditwnd.cpython-36.pyc

diff.bin_not_shown

BIN
__pycache__/pauseactioneditwnd.cpython-36.pyc

diff.bin_not_shown

BIN
__pycache__/profileeditwnd.cpython-36.pyc

diff.bin_not_shown

BIN
__pycache__/profileexecutor.cpython-36.pyc

diff.bin_not_shown

BIN
__pycache__/ui_commandeditwnd.cpython-36.pyc

diff.bin_not_shown

BIN
__pycache__/ui_keyactioneditwnd.cpython-36.pyc

diff.bin_not_shown

BIN
__pycache__/ui_mainwnd.cpython-36.pyc

diff.bin_not_shown

BIN
__pycache__/ui_mouseactioneditwnd.cpython-36.pyc

diff.bin_not_shown

BIN
__pycache__/ui_pauseactioneditwnd.cpython-36.pyc

diff.bin_not_shown

BIN
__pycache__/ui_profileeditwnd.cpython-36.pyc

diff.bin_not_shown

5
command.list

@ -0,0 +1,5 @@
go /1e-2/
back /1e-3/
left /1e-10/
right /1e-3/
stop /1e-8/

2
commandeditwnd.py

@ -33,6 +33,7 @@ class CommandEditWnd(QDialog):
self.m_command = {}
if p_command != None:
self.ui.say.setText(p_command['name'])
self.ui.thresholdSpin.setValue(p_command['threshold'])
w_actions = p_command['actions']
for w_action in w_actions:
w_jsonAction = json.dumps(w_action)
@ -152,6 +153,7 @@ class CommandEditWnd(QDialog):
w_actions.append(w_action)
self.m_command['actions'] = w_actions
self.m_command['async'] = self.ui.asyncChk.isChecked()
self.m_command['threshold'] = self.ui.thresholdSpin.value()
if self.ui.oneExe.isChecked():
self.m_command['repeat'] = 1
elif self.ui.continueExe.isChecked():

26
commandeditwnd.ui

@ -93,6 +93,32 @@
<item>
<widget class="QLineEdit" name="say"/>
</item>
<item>
<spacer name="horizontalSpacer_8">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>50</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>threshold: </string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="thresholdSpin"/>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">

64
main.py

@ -26,14 +26,13 @@ class MainWnd(QWidget):
self.ui.ok.clicked.connect(self.slotOK)
self.ui.cancel.clicked.connect(self.slotCancel)
self.m_profileExecutor.start()
if self.loadFromDatabase() > 0 :
# if self.loadTestProfiles() > 0:
w_jsonProfile = self.ui.profileCbx.itemData(0)
if w_jsonProfile != None:
self.m_activeProfile = json.loads(w_jsonProfile)
self.m_profileExecutor.setProfile(self.m_activeProfile)
self.m_profileExecutor.start()
def saveToDatabase(self):
w_profiles = []
@ -66,43 +65,48 @@ class MainWnd(QWidget):
w_carProfileDict = {
"name": "car game",
"commands": [
{'name': 'up',
{'name': 'forward',
'actions': [
{'name': 'key action', 'key': 'up', 'type': 1},
{'name': 'pause action', 'time': 0.03}
{'name': 'key action', 'key': 'up', 'type': 1}
],
'repeat': 1,
'async': False,
'threshold': 12
},
{'name': 'back',
'actions': [
{'name': 'key action', 'key': 'down', 'type': 1}
],
'repeat': 1,
'async': False
'async': False,
'threshold': 3
},
{'name': 'left',
'actions': [{'name': 'key action', 'key': 'right', 'type': 0},
{'name': 'pause action', 'time': 0.03},
{'name': 'key action', 'key': 'left', 'type': 1},
{'name': 'pause action', 'time': 0.03}
],
'repeat': 1,
'async': False
'async': False,
'threshold': 10
},
{'name': 'right',
'actions': [{'name': 'key action', 'key': 'left', 'type': 0},
{'name': 'pause action', 'time': 0.03},
{'name': 'key action', 'key': 'right', 'type': 1},
{'name': 'pause action', 'time': 0.03}
],
'repeat': 1,
'async': False
'async': False,
'threshold': 3
},
{'name': 'stop',
'actions': [
{'name': 'key action', 'key': 'left', 'type': 0},
{'name': 'pause action', 'time': 0.03},
{'name': 'key action', 'key': 'right', 'type': 0},
{'name': 'pause action', 'time': 0.03},
{'name': 'key action', 'key': 'up', 'type': 0},
{'name': 'pause action', 'time': 0.03}
{'name': 'key action', 'key': 'down', 'type': 0}
],
'repeat': 1,
'async': False
'async': False,
'threshold': 8
}
]
}
@ -117,7 +121,8 @@ class MainWnd(QWidget):
{'name': 'pause action', 'time': 0.01}
],
'repeat': -1,
'async': True
'async': True,
'threshold': 3
},
{'name': 'left',
'actions': [
@ -126,7 +131,8 @@ class MainWnd(QWidget):
{'name': 'pause action', 'time': 0.005}
],
'repeat': -1,
'async': True
'async': True,
'threshold': 3
},
{'name': 'right',
'actions': [
@ -135,7 +141,8 @@ class MainWnd(QWidget):
{'name': 'pause action', 'time': 0.005}
],
'repeat': -1,
'async': True
'async': True,
'threshold': 3
},
{'name': 'down',
'actions': [
@ -144,7 +151,8 @@ class MainWnd(QWidget):
{'name': 'pause action', 'time': 0.005}
],
'repeat': -1,
'async': True
'async': True,
'threshold': 3
},
{'name': 'shoot',
'actions': [
@ -152,7 +160,8 @@ class MainWnd(QWidget):
{'name': 'pause action', 'time': 0.03}
],
'repeat': 1,
'async': False
'async': False,
'threshold': 3
},
{'name': 'stop',
'actions': [
@ -163,7 +172,8 @@ class MainWnd(QWidget):
{'name': 'mouse click action', 'button': 'left', 'type': 0}
],
'repeat': 1,
'async': False
'async': False,
'threshold': 3
}
]
}
@ -190,6 +200,7 @@ class MainWnd(QWidget):
w_profileEditWnd = ProfileEditWnd(None, self)
if w_profileEditWnd.exec() == QDialog.Accepted:
w_profile = w_profileEditWnd.m_profile
self.m_profileExecutor.setProfile(w_profile)
self.ui.profileCbx.addItem(w_profile['name'])
w_jsonProfile = json.dumps(w_profile)
self.ui.profileCbx.setItemData(self.ui.profileCbx.count()-1, w_jsonProfile)
@ -201,6 +212,7 @@ class MainWnd(QWidget):
w_profileEditWnd = ProfileEditWnd(w_profile, self)
if w_profileEditWnd.exec() == QDialog.Accepted:
w_profile = w_profileEditWnd.m_profile
self.m_profileExecutor.setProfile(w_profile)
self.ui.profileCbx.setItemText(w_idx, w_profile['name'])
w_jsonProfile = json.dumps(w_profile)
self.ui.profileCbx.setItemData(w_idx, w_jsonProfile)
@ -210,6 +222,12 @@ class MainWnd(QWidget):
if w_curIdx >= 0:
self.ui.profileCbx.removeItem(w_curIdx)
w_curIdx = self.ui.profileCbx.currentIndex()
if w_curIdx >= 0:
w_jsonProfile = self.ui.profileCbx.itemData(w_curIdx)
w_profile = json.loads(w_jsonProfile)
self.m_profileExecutor.setProfile(w_profile)
def slotListeningEnabled(self, p_enabled):
if p_enabled:
self.m_profileExecutor.setEnableListening(True)
@ -218,9 +236,11 @@ class MainWnd(QWidget):
def slotOK(self):
self.saveToDatabase()
self.m_profileExecutor.stop()
self.close()
def slotCancel(self):
self.m_profileExecutor.stop()
self.close()
exit()

134782
model/en-us/cmudict-en-us.dict

File diff suppressed because it is too large

BIN
model/en-us/en-us-phone.lm.bin

diff.bin_not_shown

BIN
model/en-us/en-us.lm.bin

diff.bin_not_shown

34
model/en-us/en-us/README

@ -0,0 +1,34 @@
/* ====================================================================
* Copyright (c) 2015 Alpha Cephei Inc. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY ALPHA CEPHEI INC. ``AS IS'' AND.
* ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,.
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ALPHA CEPHEI INC.
* NOR ITS EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT.
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,.
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY.
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT.
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE.
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ====================================================================
*
*/
This directory contains generic US english acoustic model trained with
latest sphinxtrain.

12
model/en-us/en-us/feat.params

@ -0,0 +1,12 @@
-lowerf 130
-upperf 6800
-nfilt 25
-transform dct
-lifter 22
-feat 1s_c_d_dd
-svspec 0-12/13-25/26-38
-agc none
-cmn batch
-varnorm no
-model ptm
-cmninit 41.00,-5.29,-0.12,5.09,2.48,-4.07,-1.37,-1.78,-5.08,-2.05,-6.45,-1.42,1.17

BIN
model/en-us/en-us/mdef

diff.bin_not_shown

After

Width:  |  Height:  |  Size: 2.8 MiB

BIN
model/en-us/en-us/means

diff.bin_not_shown

5
model/en-us/en-us/noisedict

@ -0,0 +1,5 @@
<s> SIL
</s> SIL
<sil> SIL
[NOISE] +NSN+
[SPEECH] +SPN+

BIN
model/en-us/en-us/sendump

diff.bin_not_shown

BIN
model/en-us/en-us/transition_matrices

diff.bin_not_shown

BIN
model/en-us/en-us/variances

diff.bin_not_shown

107
profileeditwnd.py

@ -48,107 +48,6 @@ class ProfileEditWnd(QDialog):
QTimer.singleShot(100, self.ui.cmdTable.resizeRowsToContents)
# def activateProfile(self, p_profile):
# if p_profile == None or p_profile == {}:
# self.ui.cmdTable.setRowCount(0)
# return
#
# w_commands = p_profile['commands']
# self.ui.cmdTable.setRowCount(len(w_commands))
# i = 0
# for w_command in w_commands:
# self.ui.cmdTable.setItem(i, 0, QTableWidgetItem(w_command['name']))
# w_text = json.dumps(w_command)
# w_item = QTableWidgetItem(w_text)
# w_item.setData(Qt.UserRole, json.dumps(w_command))
# self.ui.cmdTable.setItem(i, 1, w_item)
# i = i + 1
# self.ui.cmdTable.resizeRowsToContents()
# def updateActiveProfile(self):
# w_idx = self.m_curProfileIdx
# if w_idx < 0:
# return
#
# w_profile = {}
# w_profile['name'] = self.ui.profileCbx.itemText(w_idx)
# w_commands = []
#
# w_commandCnt = self.ui.cmdTable.rowCount()
# for w_i in range(w_commandCnt):
# w_jsonCommand = self.ui.cmdTable.item(w_i, 1).data(Qt.UserRole)
# w_command = json.loads(w_jsonCommand)
# w_commands.append(w_command)
# w_profile['commands'] = w_commands
# w_jsonProfile = json.dumps(w_profile)
# self.ui.profileCbx.setItemData(w_idx, w_jsonProfile)
# def slotSelChanged(self, p_idx):
# self.updateActiveProfile()
# w_jsonProfile = self.ui.profileCbx.itemData(p_idx)
#
# w_profile = {}
# if w_jsonProfile != None:
# w_profile = json.loads(w_jsonProfile)
#
# self.activateProfile(w_profile)
#
# self.m_curProfileIdx = p_idx
# def importProfile(self, p_profile, p_update = True):
# if p_profile == None or p_profile == {}:
# return
#
# w_profileCnt = self.ui.profileCbx.count()
# for w_idx in range(w_profileCnt):
# w_jsonProfile = self.ui.profileCbx.itemData(w_idx)
# if w_jsonProfile == None:
# continue
#
# w_profile = json.loads(w_jsonProfile)
# if p_profile['name'] == w_profile['name']:
# if p_update:
# w_jsonProfile = json.dumps(p_profile)
# self.ui.profileCbx.setItemData(w_idx, w_jsonProfile)
# return True
# return False
#
# self.ui.profileCbx.addItem(p_profile['name'])
# w_jsonProfile = json.dumps(p_profile)
# self.ui.profileCbx.setItemData(w_profileCnt, w_jsonProfile)
# return True
# def slotAddNewProfile(self):
# text, okPressed = QInputDialog.getText(self, "Get Profile Name", "Profile name:", QLineEdit.Normal, "")
# if okPressed and text != '':
# w_profile = {}
# w_profile['name'] = text
# w_profile['commands'] = []
# if self.importProfile(w_profile, False) == False:
# QMessageBox.critical(None, 'Error', 'Adding a new profile was failed')
# return
# self.ui.profileCbx.setCurrentIndex(self.ui.profileCbx.count()-1)
# def slotRemoveProfile(self):
# w_curIdx = self.ui.profileCbx.currentIndex()
# if w_curIdx >= 0:
# self.ui.cmdTable.setRowCount(0)
# self.m_curProfileIdx = -1
# self.ui.profileCbx.removeItem(w_curIdx)
#
# def slotRenameProfile(self):
# w_curIdx = self.ui.profileCbx.currentIndex()
# if w_curIdx >= 0:
# text, okPressed = QInputDialog.getText(self, "Input Dialog", "Profile name:",
# QLineEdit.Normal, self.ui.profileCbx.itemText(w_curIdx))
# if okPressed and text != '':
# self.ui.profileCbx.setItemText(w_curIdx, text)
# w_jsonProfile = self.ui.profileCbx.itemData(w_curIdx)
# w_profile = json.loads(w_jsonProfile)
# w_profile['name'] = text
# w_jsonProfile = json.dumps(w_profile)
# self.ui.profileCbx.setItemData(w_curIdx, w_jsonProfile)
def importCommand(self, p_command, p_update):
w_commandCnt = self.ui.cmdTable.rowCount()
for w_i in range(w_commandCnt):
@ -192,7 +91,11 @@ class ProfileEditWnd(QDialog):
w_cmdEditWnd = CommandEditWnd(w_command, self)
if w_cmdEditWnd.exec_() == QDialog.Accepted:
self.importCommand(w_cmdEditWnd.m_command, True)
self.ui.cmdTable.setItem(w_modelIdx.row(), 0, QTableWidgetItem(w_cmdEditWnd.m_command['name']))
w_text = json.dumps(w_cmdEditWnd.m_command)
w_item = QTableWidgetItem(w_text)
w_item.setData(Qt.UserRole, json.dumps(w_cmdEditWnd.m_command))
self.ui.cmdTable.setItem(w_modelIdx.row(), 1, w_item)
self.ui.cmdTable.resizeRowsToContents()
def slotDeleteCmd(self):

215
profileexecutor.py

@ -4,43 +4,73 @@ import time
import threading
import copy
import json
import sys, os, pyaudio
from pocketsphinx.pocketsphinx import *
from sphinxbase.sphinxbase import *
class ProfileExecutor(threading.Thread):
mouse = Controller()
def __init__(self, p_profile = None):
threading.Thread.__init__(self)
self.m_profile = p_profile
self.setProfile(p_profile)
self.m_stop = False
self.m_listening = True
self.m_cmdThreads = {}
self.m_config = Decoder.default_config()
self.m_config.set_string('-hmm', os.path.join('model', 'en-us/en-us'))
self.m_config.set_string('-dict', os.path.join('model', 'en-us/cmudict-en-us.dict'))
self.m_config.set_string('-kws', 'command.list')
p = pyaudio.PyAudio()
self.m_stream = p.open(format=pyaudio.paInt16, channels=1, rate=16000, input=True)
self.m_stream.start_stream()
# Process audio chunk by chunk. On keyword detected perform action and restart search
self.m_decoder = Decoder(self.m_config)
def setProfile(self, p_profile):
self.m_profile = p_profile
if self.m_profile == None:
return
w_commandWordFile = open('command.list', 'w')
w_commands = self.m_profile['commands']
i = 0
for w_command in w_commands:
if i != 0:
w_commandWordFile.write('\n')
w_commandWordFile.write(w_command['name'] + ' /1e-%d/' % w_command['threshold'])
i = i + 1
w_commandWordFile.close()
self.m_config.set_string('-kws', 'command.list')
def setEnableListening(self, p_enable):
self.m_listening = p_enable
def run(self):
self.m_decoder.start_utt()
while self.m_stop != True:
keyboard.wait('ctrl+alt')
if self.m_listening != True:
continue
buf = self.m_stream.read(1024)
for i in range(3):
self.doCommand('up')
time.sleep(0.5)
self.m_decoder.process_raw(buf, False, False)
self.doCommand('left')
time.sleep(0.5)
if self.m_decoder.hyp() != None:
# print([(seg.word, seg.prob, seg.start_frame, seg.end_frame) for seg in decoder.seg()])
# print("Detected keyword, restarting search")
#
# Here you run the code you want based on keyword
#
for w_seg in self.m_decoder.seg():
self.doCommand(w_seg.word.rstrip())
self.doCommand('right')
time.sleep(0.5)
self.doCommand('stop')
self.m_decoder.end_utt()
self.m_decoder.start_utt()
def stop(self):
self.m_stop = True
threading.Thread.join(self)
def doAction(self, p_action):
# {'name': 'key action', 'key': 'left', 'type': 0}
@ -157,162 +187,3 @@ class ProfileExecutor(threading.Thread):
if p_cmdName in self.m_cmdThreads.keys():
self.m_cmdThreads[p_cmdName].stop()
del self.m_cmdThreads[p_cmdName]
# def carGameTest():
# w_profileDict = {
# "name": "car game",
# "commands": [
# {'name': 'up',
# 'actions': [
# {'name': 'key action', 'key': 'up', 'type': 1},
# {'name': 'pause action', 'time': 0.03}
# ],
# 'repeat': 1,
# 'async': False
# },
# {'name': 'left',
# 'actions': [{'name': 'key action', 'key': 'right', 'type': 0},
# {'name': 'pause action', 'time': 0.03},
# {'name': 'key action', 'key': 'left', 'type': 1},
# {'name': 'pause action', 'time': 0.03}
# ],
# 'repeat': 1,
# 'async': False
# },
# {'name': 'right',
# 'actions': [{'name': 'key action', 'key': 'left', 'type': 0},
# {'name': 'pause action', 'time': 0.03},
# {'name': 'key action', 'key': 'right', 'type': 1},
# {'name': 'pause action', 'time': 0.03}
# ],
# 'repeat': 1,
# 'async': False
# },
# {'name': 'stop',
# 'actions': [
# {'name': 'key action', 'key': 'left', 'type': 0},
# {'name': 'pause action', 'time': 0.03},
# {'name': 'key action', 'key': 'right', 'type': 0},
# {'name': 'pause action', 'time': 0.03},
# {'name': 'key action', 'key': 'up', 'type': 0},
# {'name': 'pause action', 'time': 0.03}
# ],
# 'repeat': 1,
# 'async': False
# }
# ]
# }
#
# w_ProfileExecutor = ProfileExecutor(w_profileDict)
#
# print("Move to the target window and press spacebar")
# keyboard.wait('space')
#
# print("Started !")
#
# for i in range(5):
# w_ProfileExecutor.doCommand('up')
# time.sleep(0.5)
#
# w_ProfileExecutor.doCommand('left')
# time.sleep(0.5)
#
# w_ProfileExecutor.doCommand('right')
# time.sleep(0.5)
#
# w_ProfileExecutor.doCommand('stop')
#
# def airplaneGameTest():
# w_profileDict = {
# "name": "airplane game",
# "commands": [
# {'name': 'up',
# 'actions': [
# {'name': 'command stop action', 'command name': 'down'},
# {'name': 'mouse move action', 'x':0, 'y':-5, 'absolute': False},
# {'name': 'pause action', 'time': 0.01}
# ],
# 'repeat': -1,
# 'async': True
# },
# {'name': 'left',
# 'actions': [
# {'name': 'command stop action', 'command name':'right'},
# {'name': 'mouse move action', 'x':-5, 'y':0, 'absolute': False},
# {'name': 'pause action', 'time': 0.005}
# ],
# 'repeat': -1,
# 'async': True
# },
# {'name': 'right',
# 'actions': [
# {'name': 'command stop action', 'command name': 'left'},
# {'name': 'mouse move action', 'x':5, 'y':0, 'absolute': False},
# {'name': 'pause action', 'time': 0.005}
# ],
# 'repeat': -1,
# 'async': True
# },
# {'name': 'down',
# 'actions': [
# {'name': 'command stop action', 'command name': 'up'},
# {'name': 'mouse move action', 'x':0, 'y':5, 'absolute': False},
# {'name': 'pause action', 'time': 0.005}
# ],
# 'repeat': -1,
# 'async': True
# },
# {'name': 'shoot',
# 'actions': [
# {'name': 'mouse click action', 'button':'left', 'type': 1},
# {'name': 'pause action', 'time': 0.03}
# ],
# 'repeat': 1,
# 'async': False
# },
# {'name': 'stop',
# 'actions': [
# {'name': 'command stop action', 'command name': 'up'},
# {'name': 'command stop action', 'command name': 'left'},
# {'name': 'command stop action', 'command name': 'right'},
# {'name': 'command stop action', 'command name': 'down'},
# {'name': 'mouse click action', 'button': 'left', 'type': 0}
# ],
# 'repeat': 1,
# 'async': False
# }
# ]
# }
#
# w_ProfileExecutor = ProfileExecutor(w_profileDict)
#
# print("Move to the target window and press the spacebar to start")
# keyboard.wait('space')
#
# time.sleep(1)
#
# print("Started !")
#
# for i in range(3):
# w_ProfileExecutor.doCommand('shoot')
# time.sleep(1)
#
# w_ProfileExecutor.doCommand('up')
# time.sleep(1)
#
# w_ProfileExecutor.doCommand('down')
# time.sleep(0.5)
#
# w_ProfileExecutor.stopCommand('down')
#
# w_ProfileExecutor.doCommand('left')
# time.sleep(0.5)
#
# w_ProfileExecutor.doCommand('right')
# time.sleep(0.5)
#
# w_ProfileExecutor.doCommand('stop')
#
# if __name__ == "__main__":
# carGameTest()
# # airplaneGameTest()

BIN
profiles.dat

diff.bin_not_shown

301
set_kws_threshold.py

@ -0,0 +1,301 @@
"""Script for auto tuning keyword spotting thresholds in pocketsphinx"""
from __future__ import print_function
import sys
import select
import os
import termios
import contextlib
import time
import re
import numpy as np
from pocketsphinx.pocketsphinx import *
from sphinxbase.sphinxbase import *
# keyphrases found in kwlist
WORDS = []
# test case containing multiple occurances
# of words to be used as training audio
TEST_CASE = []
# Threshold values
FREQUENCY = []
# End frame of each word in input speech
NO_OF_FRAMES = []
# Recorded speech input
OUTPUT_FILENAME = 'testing_audio.wav'
def preprocess_files(dic_path, kwlist_path):
"""
Function to generate required lists and call tuning functions
"""
global WORDS, TEST_CASE, FREQUENCY
# words found in dictinary
_content = []
with open(dic_path) as _f:
_content = _f.readlines()
_content = [x.strip() for x in _content]
with open(kwlist_path) as _f:
WORDS = _f.readlines()
WORDS = [x.strip()[:x.strip().rfind(' ')] for x in WORDS]
print (WORDS)
# Loop to find out initial thresholds based on phonetics provided in dictionary
for i, _ in enumerate(WORDS):
# starting position of first phone for a word
init_pos = 0
# Count number of phones based on frequency of spaces
spaces = 0
# In case there is more than one word in a keyphrase, add phones for all words
for _m in re.finditer(' ', WORDS[i]):
indices = [j for j, s in enumerate(_content) if WORDS[i][init_pos:_m.start()]+'\t' in s]
spaces = _content[indices[0]].count(' ') + spaces + 1
init_pos = _m.start()+1
indices = [j for j, s in enumerate(_content) if WORDS[i][init_pos:]+'\t' in s]
spaces += _content[indices[0]].count(' ') + 1
# Normalizing
if spaces <= 3:
FREQUENCY.append(spaces)
else:
FREQUENCY.append(spaces * 2)
# Adding random noise in test case for better tuning
TEST_CASE = ['[RANDOM]', '[RANDOM]']
TEST_CASE.extend(WORDS)
TEST_CASE.extend(['[RANDOM]', '[RANDOM]'])
TEST_CASE.extend(WORDS)
np.random.shuffle(TEST_CASE)
print ("HERE IS YOUR TRAINING SET")
print (TEST_CASE)
# record audio
record()
write_frequency_to_file(kwlist_path)
# Analysis begins
actual_tuning(dic_path, kwlist_path, 1)
print ("Removed many false alarms. New frequency: ")
print (FREQUENCY)
print ('Moving on to missed detections')
actual_tuning(dic_path, kwlist_path, 0)
print ("Frequency tuned to the best of the script's ability. New frequency: ")
print (FREQUENCY)
_missed, _fa = process_threshold(kws_analysis(dic_path, kwlist_path))
def write_frequency_to_file(kwlist_path):
"""
update modified frequencies in kwlist file
"""
_f = open(kwlist_path, 'w')
for i, val in enumerate(FREQUENCY):
_f.write(WORDS[i] + ' /1e-' + str(val) + '/\n')
_f.close()
@contextlib.contextmanager
def raw_mode(_file):
"""
Function handle the button press on successful utterance of word by user
"""
old_attrs = termios.tcgetattr(_file.fileno())
new_attrs = old_attrs[:]
new_attrs[3] = new_attrs[3] & ~(termios.ECHO | termios.ICANON)
try:
termios.tcsetattr(_file.fileno(), termios.TCSADRAIN, new_attrs)
yield
finally:
termios.tcsetattr(_file.fileno(), termios.TCSADRAIN, old_attrs)
def record():
"""
Records user's speech with timestamp for each spoken word
"""
global NO_OF_FRAMES
# rec -c 1 -r 16000 -b 16 recording.wav
print ("-----SAY THE FOLLOWING OUT LOUD AND PRESS ENTER-----")
print (TEST_CASE[0])
os.system('rec -q -c 1 -r 16000 -b 16 ' + OUTPUT_FILENAME + ' &')
NO_OF_FRAMES.append(0)
previous = time.time()
i = 0
with raw_mode(sys.stdin):
while True:
if sys.stdin in select.select([sys.stdin], [], [], 0)[0]:
_a = sys.stdin.read(1)
if _a == '\n':
if i == len(TEST_CASE)-1:
current = time.time()
NO_OF_FRAMES.append(NO_OF_FRAMES[i] + (current - previous)*100)
previous = current
print ("STOPPING RECORDING")
time.sleep(2)
# stop Recording
os.system('pkill rec')
print (NO_OF_FRAMES)
break
else:
current = time.time()
NO_OF_FRAMES.append(NO_OF_FRAMES[i] + (current - previous)*100)
previous = current
i = i+1
print ("-----SAY THE FOLLOWING OUT LOUD AND PRESS ENTER-----")
print (TEST_CASE[i])
def actual_tuning(dic_path, kwlist_path, _z):
"""
process false alarms and missed detections to tune thresholds
_z in the paramter is 1 for FA analysis and 0 for missed detection analysis
"""
# to store thresholds with minimum mismatches
minimum_inflection = [FREQUENCY[i] for i, _ in enumerate(WORDS)]
# to check whether a word's assessment has been finished or not
processed = [0 for i, _ in enumerate(WORDS)]
# get frequency of missed detections and false alarms
_missed, _fa = process_threshold(kws_analysis(dic_path, kwlist_path))
_least_negative_threshold = 1
_most_negative_threshold = 49
# Loop until there is at least one word whose assessment has not finished
while 0 in processed:
if _z == 1:
# If there is a False alarm, increase threshold
for i, val in enumerate(_fa):
if FREQUENCY[i] > _least_negative_threshold and processed[i] == 0:
if val[1] > 0:
FREQUENCY[i] -= 2
else:
processed[i] = 1
else:
processed[i] = 1
else:
# If there is a missed detection, decrease threshold
for i, val in enumerate(_missed):
if FREQUENCY[i] < _most_negative_threshold and processed[i] == 0:
if val[1] > 0:
FREQUENCY[i] += 1
else:
processed[i] = 1
else:
processed[i] = 1
write_frequency_to_file(kwlist_path)
print ('UPDATED FREQUENCY:')
print (FREQUENCY)
_previous_missed = []
_previous_missed.extend(_missed)
_previous_fa = []
_previous_fa.extend(_fa)
_missed, _fa = process_threshold(kws_analysis(dic_path, kwlist_path))
if _z == 1:
# If current readings show increase in missed detections,
# go to previous state and stop
for i, val in enumerate(_missed):
if val[1] > _previous_missed[i][1] and processed[i] == 0:
processed[i] = 1
FREQUENCY[i] += 2
else:
# If current readings show increase in false alarms,
# go to previous state and stop
for i, val in enumerate(_fa):
if val[1] > _previous_fa[i][1] and processed[i] == 0:
processed[i] = 1
FREQUENCY[i] -= 1
# If updated thresholds caused better accuracy, save them
for i, val in enumerate([_fa, _missed][_z == 0]):
if val[1] < [_previous_fa[i][1], _previous_missed][_z == 0]:
minimum_inflection[i] = FREQUENCY[i]
for i, val in enumerate([_fa, _missed][_z == 0]):
FREQUENCY[i] = minimum_inflection[i]
write_frequency_to_file(kwlist_path)
def kws_analysis(dic, kwlist):
"""
kws analysis on user speech and updated threshold values
"""
analysis_result = []
modeldir = "/usr/local/share/pocketsphinx/model/"
# Create a decoder with certain model
config = Decoder.default_config()
config.set_string('-hmm', os.path.join(modeldir, 'en-us/en-us'))
config.set_string('-dict', dic)
config.set_string('-kws', kwlist)
config.set_string('-dither', "no")
config.set_string('-logfn', '/dev/null')
config.set_string('-featparams', os.path.join(os.path.join(modeldir,
'en-us/en-us'), "feat.params"))
stream = open(OUTPUT_FILENAME, "rb")
# Process audio chunk by chunk. On keyphrase detected perform action and restart search
decoder = Decoder(config)
decoder.start_utt()
timer = 0
while True:
buf = stream.read(1024)
if buf:
decoder.process_raw(buf, False, False)
else:
break
if decoder.hyp() != None:
for seg in decoder.seg():
pass
analysis_result.append([seg.word.rstrip(), timer/320])
decoder.end_utt()
decoder.start_utt()
timer += 1024
return analysis_result
def process_threshold(analysis_result):
"""
calculate missed detections and false alarms
Argument: analysis result = kws result
"""
# stores timestamp of words which matche in both speech and kws result
_indices = []
missed = [[WORDS[i], 0] for i in range(len(WORDS))]
false_alarms = [[WORDS[i], 0] for i in range(len(WORDS))]
i = 0
for i, val in enumerate(analysis_result):
# Calculate the timestamp in speech closest to timestamp of word found by kws result
_index = min(range(len(NO_OF_FRAMES)), key=lambda l: abs(NO_OF_FRAMES[l] - val[1]))
_indices.append(_index)
if TEST_CASE[_index-1] == '[RANDOM]':
position_observer = WORDS.index(val[0])
false_alarms[position_observer][1] += 1
print ('FA Found', val[0], ' in place of RANDOM TEXT')
elif TEST_CASE[_index-1] == val[0]:
print ('DETECTED CORRECTLY', val[0])
else:
print ('FA Found', val[0], ' in place of ', TEST_CASE[_index-1])
position_original = WORDS.index(TEST_CASE[_index-1])
position_observer = WORDS.index(val[0])
missed[position_original][1] += 1
false_alarms[position_observer][1] += 1
# If speech had timestamp not mentioned in kws result, then its detection was missed
for i, val in enumerate(TEST_CASE):
if i+1 not in _indices and val != '[RANDOM]':
position_original = WORDS.index(val)
missed[position_original][1] += 1
print ('Missed ', val)
return missed, false_alarms
if __name__ == '__main__':
DIC_FILE = "/home/pankaj/catkin_ws/src/pocketsphinx/demo/voice_cmd.dic"
KWLIST_FILE = "/home/pankaj/catkin_ws/src/pocketsphinx/demo/automated.kwlist"
if len(sys.argv) == 3:
DIC_FILE = sys.argv[1]
KWLIST_FILE = sys.argv[2]
preprocess_files(DIC_FILE, KWLIST_FILE)

31
ui_commandeditwnd.py

@ -39,8 +39,16 @@ class Ui_CommandEditDialog(object):
self.say = QtWidgets.QLineEdit(CommandEditDialog)
self.say.setObjectName("say")
self.horizontalLayout.addWidget(self.say)
spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
spacerItem3 = QtWidgets.QSpacerItem(50, 20, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem3)
self.label_4 = QtWidgets.QLabel(CommandEditDialog)
self.label_4.setObjectName("label_4")
self.horizontalLayout.addWidget(self.label_4)
self.thresholdSpin = QtWidgets.QSpinBox(CommandEditDialog)
self.thresholdSpin.setObjectName("thresholdSpin")
self.horizontalLayout.addWidget(self.thresholdSpin)
spacerItem4 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem4)
self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 8)
self.horizontalLayout_9 = QtWidgets.QHBoxLayout()
self.horizontalLayout_9.setObjectName("horizontalLayout_9")
@ -48,8 +56,8 @@ class Ui_CommandEditDialog(object):
self.oneExe.setAutoExclusive(True)
self.oneExe.setObjectName("oneExe")
self.horizontalLayout_9.addWidget(self.oneExe)
spacerItem4 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_9.addItem(spacerItem4)
spacerItem5 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_9.addItem(spacerItem5)
self.gridLayout.addLayout(self.horizontalLayout_9, 6, 0, 1, 1)
self.horizontalLayout_10 = QtWidgets.QHBoxLayout()
self.horizontalLayout_10.setObjectName("horizontalLayout_10")
@ -57,8 +65,8 @@ class Ui_CommandEditDialog(object):
self.continueExe.setAutoExclusive(True)
self.continueExe.setObjectName("continueExe")
self.horizontalLayout_10.addWidget(self.continueExe)
spacerItem5 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_10.addItem(spacerItem5)
spacerItem6 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_10.addItem(spacerItem6)
self.gridLayout.addLayout(self