1
0
mirror of https://github.com/aidygus/LinVAM.git synced 2024-11-23 09:18:04 +11:00

added the initial version for voice command spotting

This commit is contained in:
rose-jinyang 2019-04-17 11:52:40 +08:00
parent 821cee0db2
commit 8279d77e42
38 changed files with 135701 additions and 307 deletions

11
.idea/LinVAM.iml Normal file
View File

@ -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 Normal file
View File

@ -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 Normal file
View File

@ -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 Normal file
View File

@ -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 Normal file
View File

@ -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>

View File

@ -1,2 +1,18 @@
# LinVAM # LinVAM
Linux Voice Activated Macro 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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

5
command.list Normal file
View File

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

View File

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

View File

@ -93,6 +93,32 @@
<item> <item>
<widget class="QLineEdit" name="say"/> <widget class="QLineEdit" name="say"/>
</item> </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> <item>
<spacer name="horizontalSpacer"> <spacer name="horizontalSpacer">
<property name="orientation"> <property name="orientation">

64
main.py
View File

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

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

File diff suppressed because it is too large Load Diff

Binary file not shown.

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

Binary file not shown.

34
model/en-us/en-us/README Normal file
View File

@ -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.

View File

@ -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 Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

BIN
model/en-us/en-us/means Normal file

Binary file not shown.

View File

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

BIN
model/en-us/en-us/sendump Normal file

Binary file not shown.

Binary file not shown.

BIN
model/en-us/en-us/variances Normal file

Binary file not shown.

View File

@ -48,107 +48,6 @@ class ProfileEditWnd(QDialog):
QTimer.singleShot(100, self.ui.cmdTable.resizeRowsToContents) 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): def importCommand(self, p_command, p_update):
w_commandCnt = self.ui.cmdTable.rowCount() w_commandCnt = self.ui.cmdTable.rowCount()
for w_i in range(w_commandCnt): for w_i in range(w_commandCnt):
@ -192,7 +91,11 @@ class ProfileEditWnd(QDialog):
w_cmdEditWnd = CommandEditWnd(w_command, self) w_cmdEditWnd = CommandEditWnd(w_command, self)
if w_cmdEditWnd.exec_() == QDialog.Accepted: 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() self.ui.cmdTable.resizeRowsToContents()
def slotDeleteCmd(self): def slotDeleteCmd(self):

View File

@ -4,43 +4,73 @@ import time
import threading import threading
import copy import copy
import json import json
import sys, os, pyaudio
from pocketsphinx.pocketsphinx import *
from sphinxbase.sphinxbase import *
class ProfileExecutor(threading.Thread): class ProfileExecutor(threading.Thread):
mouse = Controller() mouse = Controller()
def __init__(self, p_profile = None): def __init__(self, p_profile = None):
threading.Thread.__init__(self) threading.Thread.__init__(self)
self.m_profile = p_profile self.setProfile(p_profile)
self.m_stop = False self.m_stop = False
self.m_listening = True self.m_listening = True
self.m_cmdThreads = {} 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): def setProfile(self, p_profile):
self.m_profile = 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): def setEnableListening(self, p_enable):
self.m_listening = p_enable self.m_listening = p_enable
def run(self): def run(self):
self.m_decoder.start_utt()
while self.m_stop != True: while self.m_stop != True:
keyboard.wait('ctrl+alt') buf = self.m_stream.read(1024)
if self.m_listening != True:
continue
for i in range(3): self.m_decoder.process_raw(buf, False, False)
self.doCommand('up')
time.sleep(0.5)
self.doCommand('left') if self.m_decoder.hyp() != None:
time.sleep(0.5) # 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') self.m_decoder.end_utt()
time.sleep(0.5) self.m_decoder.start_utt()
self.doCommand('stop')
def stop(self): def stop(self):
self.m_stop = True self.m_stop = True
threading.Thread.join(self)
def doAction(self, p_action): def doAction(self, p_action):
# {'name': 'key action', 'key': 'left', 'type': 0} # {'name': 'key action', 'key': 'left', 'type': 0}
@ -157,162 +187,3 @@ class ProfileExecutor(threading.Thread):
if p_cmdName in self.m_cmdThreads.keys(): if p_cmdName in self.m_cmdThreads.keys():
self.m_cmdThreads[p_cmdName].stop() self.m_cmdThreads[p_cmdName].stop()
del self.m_cmdThreads[p_cmdName] 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()

Binary file not shown.

301
set_kws_threshold.py Normal file
View File

@ -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)

View File

@ -39,8 +39,16 @@ class Ui_CommandEditDialog(object):
self.say = QtWidgets.QLineEdit(CommandEditDialog) self.say = QtWidgets.QLineEdit(CommandEditDialog)
self.say.setObjectName("say") self.say.setObjectName("say")
self.horizontalLayout.addWidget(self.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.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.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 8)
self.horizontalLayout_9 = QtWidgets.QHBoxLayout() self.horizontalLayout_9 = QtWidgets.QHBoxLayout()
self.horizontalLayout_9.setObjectName("horizontalLayout_9") self.horizontalLayout_9.setObjectName("horizontalLayout_9")
@ -48,8 +56,8 @@ class Ui_CommandEditDialog(object):
self.oneExe.setAutoExclusive(True) self.oneExe.setAutoExclusive(True)
self.oneExe.setObjectName("oneExe") self.oneExe.setObjectName("oneExe")
self.horizontalLayout_9.addWidget(self.oneExe) self.horizontalLayout_9.addWidget(self.oneExe)
spacerItem4 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) spacerItem5 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_9.addItem(spacerItem4) self.horizontalLayout_9.addItem(spacerItem5)
self.gridLayout.addLayout(self.horizontalLayout_9, 6, 0, 1, 1) self.gridLayout.addLayout(self.horizontalLayout_9, 6, 0, 1, 1)
self.horizontalLayout_10 = QtWidgets.QHBoxLayout() self.horizontalLayout_10 = QtWidgets.QHBoxLayout()
self.horizontalLayout_10.setObjectName("horizontalLayout_10") self.horizontalLayout_10.setObjectName("horizontalLayout_10")
@ -57,8 +65,8 @@ class Ui_CommandEditDialog(object):
self.continueExe.setAutoExclusive(True) self.continueExe.setAutoExclusive(True)
self.continueExe.setObjectName("continueExe") self.continueExe.setObjectName("continueExe")
self.horizontalLayout_10.addWidget(self.continueExe) self.horizontalLayout_10.addWidget(self.continueExe)
spacerItem5 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) spacerItem6 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_10.addItem(spacerItem5) self.horizontalLayout_10.addItem(spacerItem6)
self.gridLayout.addLayout(self.horizontalLayout_10, 7, 0, 1, 1) self.gridLayout.addLayout(self.horizontalLayout_10, 7, 0, 1, 1)
self.horizontalLayout_11 = QtWidgets.QHBoxLayout() self.horizontalLayout_11 = QtWidgets.QHBoxLayout()
self.horizontalLayout_11.setObjectName("horizontalLayout_11") self.horizontalLayout_11.setObjectName("horizontalLayout_11")
@ -73,8 +81,8 @@ class Ui_CommandEditDialog(object):
self.label_3 = QtWidgets.QLabel(CommandEditDialog) self.label_3 = QtWidgets.QLabel(CommandEditDialog)
self.label_3.setObjectName("label_3") self.label_3.setObjectName("label_3")
self.horizontalLayout_11.addWidget(self.label_3) self.horizontalLayout_11.addWidget(self.label_3)
spacerItem6 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) spacerItem7 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_11.addItem(spacerItem6) self.horizontalLayout_11.addItem(spacerItem7)
self.gridLayout.addLayout(self.horizontalLayout_11, 8, 0, 1, 1) self.gridLayout.addLayout(self.horizontalLayout_11, 8, 0, 1, 1)
self.horizontalLayout_7 = QtWidgets.QHBoxLayout() self.horizontalLayout_7 = QtWidgets.QHBoxLayout()
self.horizontalLayout_7.setObjectName("horizontalLayout_7") self.horizontalLayout_7.setObjectName("horizontalLayout_7")
@ -129,14 +137,14 @@ class Ui_CommandEditDialog(object):
self.asyncChk.setChecked(True) self.asyncChk.setChecked(True)
self.asyncChk.setObjectName("asyncChk") self.asyncChk.setObjectName("asyncChk")
self.horizontalLayout_8.addWidget(self.asyncChk) self.horizontalLayout_8.addWidget(self.asyncChk)
spacerItem7 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) spacerItem8 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_8.addItem(spacerItem7) self.horizontalLayout_8.addItem(spacerItem8)
self.gridLayout.addLayout(self.horizontalLayout_8, 5, 0, 1, 3) self.gridLayout.addLayout(self.horizontalLayout_8, 5, 0, 1, 3)
self.horizontalLayout_12 = QtWidgets.QHBoxLayout() self.horizontalLayout_12 = QtWidgets.QHBoxLayout()
self.horizontalLayout_12.setSpacing(20) self.horizontalLayout_12.setSpacing(20)
self.horizontalLayout_12.setObjectName("horizontalLayout_12") self.horizontalLayout_12.setObjectName("horizontalLayout_12")
spacerItem8 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) spacerItem9 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_12.addItem(spacerItem8) self.horizontalLayout_12.addItem(spacerItem9)
self.ok = QtWidgets.QPushButton(CommandEditDialog) self.ok = QtWidgets.QPushButton(CommandEditDialog)
self.ok.setMinimumSize(QtCore.QSize(130, 0)) self.ok.setMinimumSize(QtCore.QSize(130, 0))
self.ok.setAutoDefault(False) self.ok.setAutoDefault(False)
@ -157,6 +165,7 @@ class Ui_CommandEditDialog(object):
CommandEditDialog.setWindowTitle(_translate("CommandEditDialog", "Command Edit Dialog")) CommandEditDialog.setWindowTitle(_translate("CommandEditDialog", "Command Edit Dialog"))
self.label_2.setText(_translate("CommandEditDialog", "When this command excutes, do the following sequence:")) self.label_2.setText(_translate("CommandEditDialog", "When this command excutes, do the following sequence:"))
self.label.setText(_translate("CommandEditDialog", "When I say :")) self.label.setText(_translate("CommandEditDialog", "When I say :"))
self.label_4.setText(_translate("CommandEditDialog", "threshold: "))
self.oneExe.setText(_translate("CommandEditDialog", "This command executes once")) self.oneExe.setText(_translate("CommandEditDialog", "This command executes once"))
self.continueExe.setText(_translate("CommandEditDialog", "This command repeats continuously")) self.continueExe.setText(_translate("CommandEditDialog", "This command repeats continuously"))
self.repeatExe.setText(_translate("CommandEditDialog", "This command repeats")) self.repeatExe.setText(_translate("CommandEditDialog", "This command repeats"))