From 2a641364bf8fc3cc4069d2d2c42b75241e6dc3f2 Mon Sep 17 00:00:00 2001 From: "Clayton G. Hobbs" Date: Sat, 26 Dec 2015 22:43:12 -0500 Subject: Removed QT interface, renamed interfaces to Kaylee I'm a GTK man myself. I don't know if I have Python QT bindings installed on any of my computers. It follows then that I would not maintain the QT interface well, let alone use it at all. It has therefore been removed to avoid having someone try to use it only to find that it's broken. --- Blather.py | 252 --------------------------------------------------------- GtkTrayUI.py | 97 ---------------------- GtkUI.py | 109 ------------------------- QtUI.py | 115 -------------------------- Recognizer.py | 65 --------------- blather.py | 253 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ gtktrayui.py | 105 ++++++++++++++++++++++++ gtkui.py | 111 ++++++++++++++++++++++++++ recognizer.py | 66 +++++++++++++++ 9 files changed, 535 insertions(+), 638 deletions(-) delete mode 100755 Blather.py delete mode 100644 GtkTrayUI.py delete mode 100644 GtkUI.py delete mode 100644 QtUI.py delete mode 100755 Recognizer.py create mode 100755 blather.py create mode 100644 gtktrayui.py create mode 100644 gtkui.py create mode 100755 recognizer.py diff --git a/Blather.py b/Blather.py deleted file mode 100755 index 1f59ee0..0000000 --- a/Blather.py +++ /dev/null @@ -1,252 +0,0 @@ -#!/usr/bin/env python2 - -# -- this code is licensed GPLv3 -# Copyright 2013 Jezra - -import sys -import signal -from gi.repository import GObject -import os.path -import subprocess -from optparse import OptionParser -try: - import yaml -except: - print "YAML is not supported. ~/.config/blather/options.yaml will not function" - -# Where are the files? -conf_dir = os.path.expanduser("~/.config/blather") -lang_dir = os.path.join(conf_dir, "language") -command_file = os.path.join(conf_dir, "commands.conf") -strings_file = os.path.join(conf_dir, "sentences.corpus") -history_file = os.path.join(conf_dir, "blather.history") -opt_file = os.path.join(conf_dir, "options.yaml") -lang_file = os.path.join(lang_dir,'lm') -dic_file = os.path.join(lang_dir,'dic') -# Make the lang_dir if it doesn't exist -if not os.path.exists(lang_dir): - os.makedirs(lang_dir) - -class Blather: - - def __init__(self, opts): - # Import the recognizer so Gst doesn't clobber our -h - from Recognizer import Recognizer - self.ui = None - self.options = {} - ui_continuous_listen = False - self.continuous_listen = False - - self.commands = {} - - # Read the commands - self.read_commands() - - # Load the options file - self.load_options() - - # Merge the opts - for k,v in opts.__dict__.items(): - if (not k in self.options) or opts.override: - self.options[k] = v - - if self.options['interface'] != None: - if self.options['interface'] == "q": - from QtUI import UI - elif self.options['interface'] == "g": - from GtkUI import UI - elif self.options['interface'] == "gt": - from GtkTrayUI import UI - else: - print "no GUI defined" - sys.exit() - - self.ui = UI(args, self.options['continuous']) - self.ui.connect("command", self.process_command) - #can we load the icon resource? - icon = self.load_resource("icon.png") - if icon: - self.ui.set_icon_active_asset(icon) - #can we load the icon_inactive resource? - icon_inactive = self.load_resource("icon_inactive.png") - if icon_inactive: - self.ui.set_icon_inactive_asset(icon_inactive) - - if self.options['history']: - self.history = [] - - # Create the recognizer - try: - self.recognizer = Recognizer(lang_file, dic_file, self.options['microphone']) - except Exception, e: - #no recognizer? bummer - print 'error making recognizer' - sys.exit() - - self.recognizer.connect('finished', self.recognizer_finished) - - print "Using Options: ", self.options - - def read_commands(self): - # Read the commands file - file_lines = open(command_file) - strings = open(strings_file, "w") - for line in file_lines: - print line - # Trim the white spaces - line = line.strip() - # If the line has length and the first char isn't a hash - if len(line) and line[0]!="#": - # This is a parsible line - (key,value) = line.split(":",1) - print key, value - self.commands[key.strip().lower()] = value.strip() - strings.write( key.strip()+"\n") - # Close the strings file - strings.close() - - def load_options(self): - # Is there an opt file? - try: - opt_fh = open(opt_file) - text = opt_fh.read() - self.options = yaml.load(text) - except: - pass - - - def log_history(self,text): - if self.options['history']: - self.history.append(text) - if len(self.history) > self.options['history']: - # Pop off the first item - self.history.pop(0) - - # Open and truncate the blather history file - hfile = open(history_file, "w") - for line in self.history: - hfile.write( line+"\n") - # Close the file - hfile.close() - - def run_command(self, cmd): - '''Print the command, then run it''' - print cmd - subprocess.call(cmd, shell=True) - - def recognizer_finished(self, recognizer, text): - t = text.lower() - # Is there a matching command? - if self.commands.has_key( t ): - # Run the valid_sentence_command if there is a valid sentence command - if self.options['valid_sentence_command']: - subprocess.call(self.options['valid_sentence_command'], shell=True) - cmd = self.commands[t] - # Should we be passing words? - if self.options['pass_words']: - cmd += " " + t - self.run_command(cmd) - else: - self.run_command(cmd) - self.log_history(text) - else: - # Run the invalid_sentence_command if there is an invalid sentence command - if self.options['invalid_sentence_command']: - subprocess.call(self.options['invalid_sentence_command'], shell=True) - print "no matching command %s" % t - # If there is a UI and we are not continuous listen - if self.ui: - if not self.continuous_listen: - # Stop listening - self.recognizer.pause() - # Let the UI know that there is a finish - self.ui.finished(t) - - def run(self): - if self.ui: - self.ui.run() - else: - blather.recognizer.listen() - - def quit(self): - sys.exit() - - def process_command(self, UI, command): - print command - if command == "listen": - self.recognizer.listen() - elif command == "stop": - self.recognizer.pause() - elif command == "continuous_listen": - self.continuous_listen = True - self.recognizer.listen() - elif command == "continuous_stop": - self.continuous_listen = False - self.recognizer.pause() - elif command == "quit": - self.quit() - - def load_resource(self,string): - local_data = os.path.join(os.path.dirname(__file__), 'data') - paths = ["/usr/share/blather/","/usr/local/share/blather", local_data] - for path in paths: - resource = os.path.join(path, string) - if os.path.exists( resource ): - return resource - # If we get this far, no resource was found - return False - - -if __name__ == "__main__": - parser = OptionParser() - parser.add_option("-i", "--interface", type="string", dest="interface", - action='store', - help="Interface to use (if any). 'q' for Qt, 'g' for GTK, 'gt' for GTK system tray icon") - - parser.add_option("-c", "--continuous", - action="store_true", dest="continuous", default=False, - help="starts interface with 'continuous' listen enabled") - - parser.add_option("-p", "--pass-words", - action="store_true", dest="pass_words", default=False, - help="passes the recognized words as arguments to the shell command") - - parser.add_option("-o", "--override", - action="store_true", dest="override", default=False, - help="override config file with command line options") - - parser.add_option("-H", "--history", type="int", - action="store", dest="history", - help="number of commands to store in history file") - - parser.add_option("-m", "--microphone", type="int", - action="store", dest="microphone", default=None, - help="Audio input card to use (if other than system default)") - - parser.add_option("--valid-sentence-command", type="string", dest="valid_sentence_command", - action='store', - help="command to run when a valid sentence is detected") - - parser.add_option( "--invalid-sentence-command", type="string", dest="invalid_sentence_command", - action='store', - help="command to run when an invalid sentence is detected") - - (options, args) = parser.parse_args() - # Make our blather object - blather = Blather(options) - # Init gobject threads - GObject.threads_init() - # We want a main loop - main_loop = GObject.MainLoop() - # Handle sigint - signal.signal(signal.SIGINT, signal.SIG_DFL) - # Run the blather - blather.run() - # Start the main loop - try: - main_loop.run() - except: - print "time to quit" - main_loop.quit() - sys.exit() - diff --git a/GtkTrayUI.py b/GtkTrayUI.py deleted file mode 100644 index dda153d..0000000 --- a/GtkTrayUI.py +++ /dev/null @@ -1,97 +0,0 @@ -import sys -from gi.repository import GObject -# Gtk -from gi.repository import Gtk, Gdk - -class UI(GObject.GObject): - __gsignals__ = { - 'command' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE, (GObject.TYPE_STRING,)) - } - - def __init__(self, args, continuous): - GObject.GObject.__init__(self) - self.continuous = continuous - - self.statusicon = Gtk.StatusIcon() - self.statusicon.set_title("Blather") - self.statusicon.set_name("Blather") - self.statusicon.set_tooltip_text("Blather - Idle") - self.statusicon.set_has_tooltip(True) - self.statusicon.connect("activate", self.continuous_toggle) - self.statusicon.connect("popup-menu", self.popup_menu) - - self.menu = Gtk.Menu() - self.menu_listen = Gtk.MenuItem('Listen') - self.menu_continuous = Gtk.CheckMenuItem('Continuous') - self.menu_quit = Gtk.MenuItem('Quit') - self.menu.append(self.menu_listen) - self.menu.append(self.menu_continuous) - self.menu.append(self.menu_quit) - self.menu_listen.connect("activate", self.toggle_listen) - self.menu_continuous.connect("toggled", self.toggle_continuous) - self.menu_quit.connect("activate", self.quit) - self.menu.show_all() - - def continuous_toggle(self, item): - checked = self.menu_continuous.get_active() - self.menu_continuous.set_active(not checked) - - def toggle_continuous(self, item): - checked = self.menu_continuous.get_active() - self.menu_listen.set_sensitive(not checked) - if checked: - self.menu_listen.set_label("Listen") - self.emit('command', "continuous_listen") - self.statusicon.set_tooltip_text("Blather - Listening") - self.set_icon_active() - else: - self.set_icon_inactive() - self.statusicon.set_tooltip_text("Blather - Idle") - self.emit('command', "continuous_stop") - - def toggle_listen(self, item): - val = self.menu_listen.get_label() - if val == "Listen": - self.emit("command", "listen") - self.menu_listen.set_label("Stop") - self.statusicon.set_tooltip_text("Blather - Listening") - else: - self.icon_inactive() - self.menu_listen.set_label("Listen") - self.emit("command", "stop") - self.statusicon.set_tooltip_text("Blather - Idle") - - def popup_menu(self, item, button, time): - self.menu.popup(None, None, Gtk.StatusIcon.position_menu, item, button, time) - - def run(self): - # Set the icon - self.set_icon_inactive() - if self.continuous: - self.menu_continuous.set_active(True) - self.set_icon_active() - else: - self.menu_continuous.set_active(False) - self.statusicon.set_visible(True) - - def quit(self, item): - self.statusicon.set_visible(False) - self.emit("command", "quit") - - def finished(self, text): - if not self.menu_continuous.get_active(): - self.menu_listen.set_label("Listen") - self.statusicon.set_from_icon_name("blather_stopped") - self.statusicon.set_tooltip_text("Blather - Idle") - - def set_icon_active_asset(self, i): - self.icon_active = i - - def set_icon_inactive_asset(self, i): - self.icon_inactive = i - - def set_icon_active(self): - self.statusicon.set_from_file(self.icon_active) - - def set_icon_inactive(self): - self.statusicon.set_from_file(self.icon_inactive) diff --git a/GtkUI.py b/GtkUI.py deleted file mode 100644 index 2e0fc17..0000000 --- a/GtkUI.py +++ /dev/null @@ -1,109 +0,0 @@ -# This is part of Blather -# -- this code is licensed GPLv3 -# Copyright 2013 Jezra -import sys -from gi.repository import GObject -# Gtk -from gi.repository import Gtk, Gdk - -class UI(GObject.GObject): - __gsignals__ = { - 'command' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE, (GObject.TYPE_STRING,)) - } - - def __init__(self,args, continuous): - GObject.GObject.__init__(self) - self.continuous = continuous - # Make a window - self.window = Gtk.Window(Gtk.WindowType.TOPLEVEL) - self.window.connect("delete_event", self.delete_event) - # Give the window a name - self.window.set_title("BlatherGtk") - self.window.set_resizable(False) - - layout = Gtk.VBox() - self.window.add(layout) - # Make a listen/stop button - self.lsbutton = Gtk.Button("Listen") - layout.add(self.lsbutton) - # Make a continuous button - self.ccheckbox = Gtk.CheckButton("Continuous Listen") - layout.add(self.ccheckbox) - - # Connect the buttons - self.lsbutton.connect("clicked",self.lsbutton_clicked) - self.ccheckbox.connect("clicked",self.ccheckbox_clicked) - - # Add a label to the UI to display the last command - self.label = Gtk.Label() - layout.add(self.label) - - # Create an accellerator group for this window - accel = Gtk.AccelGroup() - # Add the ctrl+q to quit - accel.connect(Gdk.keyval_from_name('q'), Gdk.ModifierType.CONTROL_MASK, - Gtk.AccelFlags.VISIBLE, self.accel_quit) - # Lock the group - accel.lock() - # Add the group to the window - self.window.add_accel_group(accel) - - def ccheckbox_clicked(self, widget): - checked = self.ccheckbox.get_active() - self.lsbutton.set_sensitive(not checked) - if checked: - self.lsbutton_stopped() - self.emit('command', "continuous_listen") - self.set_icon_active() - else: - self.emit('command', "continuous_stop") - self.set_icon_inactive() - - def lsbutton_stopped(self): - self.lsbutton.set_label("Listen") - - def lsbutton_clicked(self, button): - val = self.lsbutton.get_label() - if val == "Listen": - self.emit("command", "listen") - self.lsbutton.set_label("Stop") - # Clear the label - self.label.set_text("") - self.set_icon_active() - else: - self.lsbutton_stopped() - self.emit("command", "stop") - self.set_icon_inactive() - - def run(self): - # Set the default icon - self.set_icon_inactive() - self.window.show_all() - if self.continuous: - self.set_icon_active() - self.ccheckbox.set_active(True) - - def accel_quit(self, accel_group, acceleratable, keyval, modifier): - self.emit("command", "quit") - - def delete_event(self, x, y): - self.emit("command", "quit") - - def finished(self, text): - # If the continuous isn't pressed - if not self.ccheckbox.get_active(): - self.lsbutton_stopped() - self.set_icon_inactive() - self.label.set_text(text) - - def set_icon_active_asset(self, i): - self.icon_active = i - - def set_icon_inactive_asset(self, i): - self.icon_inactive = i - - def set_icon_active(self): - Gtk.Window.set_default_icon_from_file(self.icon_active) - - def set_icon_inactive(self): - Gtk.Window.set_default_icon_from_file(self.icon_inactive) diff --git a/QtUI.py b/QtUI.py deleted file mode 100644 index 728ff80..0000000 --- a/QtUI.py +++ /dev/null @@ -1,115 +0,0 @@ -#This is part of Blather -# -- this code is licensed GPLv3 -# Copyright 2013 Jezra -import sys -import gobject -# Qt stuff -from PySide.QtCore import Signal, Qt -from PySide.QtGui import QApplication, QWidget, QMainWindow, QVBoxLayout -from PySide.QtGui import QLabel, QPushButton, QCheckBox, QIcon, QAction - -class UI(gobject.GObject): - __gsignals__ = { - 'command' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_STRING,)) - } - - def __init__(self,args,continuous): - self.continuous = continuous - gobject.GObject.__init__(self) - #start by making our app - self.app = QApplication(args) - #make a window - self.window = QMainWindow() - #give the window a name - self.window.setWindowTitle("BlatherQt") - self.window.setMaximumSize(400,200) - center = QWidget() - self.window.setCentralWidget(center) - - layout = QVBoxLayout() - center.setLayout(layout) - #make a listen/stop button - self.lsbutton = QPushButton("Listen") - layout.addWidget(self.lsbutton) - #make a continuous button - self.ccheckbox = QCheckBox("Continuous Listen") - layout.addWidget(self.ccheckbox) - - #connect the buttons - self.lsbutton.clicked.connect(self.lsbutton_clicked) - self.ccheckbox.clicked.connect(self.ccheckbox_clicked) - - #add a label to the UI to display the last command - self.label = QLabel() - layout.addWidget(self.label) - - #add the actions for quiting - quit_action = QAction(self.window) - quit_action.setShortcut('Ctrl+Q') - quit_action.triggered.connect(self.accel_quit) - self.window.addAction(quit_action) - - def accel_quit(self): - #emit the quit - self.emit("command", "quit") - - def ccheckbox_clicked(self): - checked = self.ccheckbox.isChecked() - if checked: - #disable lsbutton - self.lsbutton.setEnabled(False) - self.lsbutton_stopped() - self.emit('command', "continuous_listen") - self.set_icon_active() - else: - self.lsbutton.setEnabled(True) - self.emit('command', "continuous_stop") - self.set_icon_inactive() - - def lsbutton_stopped(self): - self.lsbutton.setText("Listen") - - def lsbutton_clicked(self): - val = self.lsbutton.text() - if val == "Listen": - self.emit("command", "listen") - self.lsbutton.setText("Stop") - #clear the label - self.label.setText("") - self.set_icon_active() - else: - self.lsbutton_stopped() - self.emit("command", "stop") - self.set_icon_inactive() - - def run(self): - self.set_icon_inactive() - self.window.show() - if self.continuous: - self.set_icon_active() - self.ccheckbox.setCheckState(Qt.Checked) - self.ccheckbox_clicked() - self.app.exec_() - self.emit("command", "quit") - - def finished(self, text): - #if the continuous isn't pressed - if not self.ccheckbox.isChecked(): - self.lsbutton_stopped() - self.label.setText(text) - - def set_icon(self, icon): - self.window.setWindowIcon(QIcon(icon)) - - def set_icon_active_asset(self, i): - self.icon_active = i - - def set_icon_inactive_asset(self, i): - self.icon_inactive = i - - def set_icon_active(self): - self.window.setWindowIcon(QIcon(self.icon_active)) - - def set_icon_inactive(self): - self.window.setWindowIcon(QIcon(self.icon_inactive)) - diff --git a/Recognizer.py b/Recognizer.py deleted file mode 100755 index e962ea3..0000000 --- a/Recognizer.py +++ /dev/null @@ -1,65 +0,0 @@ -# This is part of Blather -# -- this code is licensed GPLv3 -# Copyright 2013 Jezra - -import gi -gi.require_version('Gst', '1.0') -from gi.repository import GObject, Gst -GObject.threads_init() -Gst.init(None) -import os.path -import sys - -# Define some global variables -this_dir = os.path.dirname( os.path.abspath(__file__) ) - - -class Recognizer(GObject.GObject): - __gsignals__ = { - 'finished' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE, (GObject.TYPE_STRING,)) - } - - def __init__(self, language_file, dictionary_file, src = None): - GObject.GObject.__init__(self) - self.commands = {} - if src: - audio_src = 'alsasrc device="hw:%d,0"' % (src) - else: - audio_src = 'autoaudiosrc' - - # Build the pipeline - cmd = audio_src+' ! audioconvert ! audioresample ! pocketsphinx name=asr ! appsink sync=false' - try: - self.pipeline=Gst.parse_launch( cmd ) - except Exception, e: - print e.message - print "You may need to install gstreamer1.0-pocketsphinx" - raise e - - bus = self.pipeline.get_bus() - bus.add_signal_watch() - - # Get the Auto Speech Recognition piece - asr=self.pipeline.get_by_name('asr') - bus.connect('message::element', self.result) - asr.set_property('lm', language_file) - asr.set_property('dict', dictionary_file) - asr.set_property('configured', True) - - def listen(self): - self.pipeline.set_state(Gst.State.PLAYING) - - def pause(self): - self.pipeline.set_state(Gst.State.PAUSED) - - def result(self, bus, msg): - msg_struct = msg.get_structure() - # Ignore messages that aren't from pocketsphinx - msgtype = msg_struct.get_name() - if msgtype != 'pocketsphinx': - return - - # If we have a final command, send it for processing - command = msg_struct.get_string('hypothesis') - if command != '' and msg_struct.get_boolean('final')[1]: - self.emit("finished", command) diff --git a/blather.py b/blather.py new file mode 100755 index 0000000..3853314 --- /dev/null +++ b/blather.py @@ -0,0 +1,253 @@ +#!/usr/bin/env python2 + +# This is part of Kaylee +# -- this code is licensed GPLv3 +# Copyright 2013 Jezra +# Copyright 2015 Clayton G. Hobbs + +import sys +import signal +from gi.repository import GObject +import os.path +import subprocess +from optparse import OptionParser +try: + import yaml +except: + print "YAML is not supported. ~/.config/blather/options.yaml will not function" + +from recognizer import Recognizer + +# Where are the files? +conf_dir = os.path.expanduser("~/.config/blather") +lang_dir = os.path.join(conf_dir, "language") +command_file = os.path.join(conf_dir, "commands.conf") +strings_file = os.path.join(conf_dir, "sentences.corpus") +history_file = os.path.join(conf_dir, "blather.history") +opt_file = os.path.join(conf_dir, "options.yaml") +lang_file = os.path.join(lang_dir,'lm') +dic_file = os.path.join(lang_dir,'dic') +# Make the lang_dir if it doesn't exist +if not os.path.exists(lang_dir): + os.makedirs(lang_dir) + +class Blather: + + def __init__(self, opts): + # Import the recognizer so Gst doesn't clobber our -h + self.ui = None + self.options = {} + ui_continuous_listen = False + self.continuous_listen = False + + self.commands = {} + + # Read the commands + self.read_commands() + + # Load the options file + self.load_options() + + # Merge the opts + for k,v in opts.__dict__.items(): + if (not k in self.options) or opts.override: + self.options[k] = v + + if self.options['interface'] != None: + if self.options['interface'] == "g": + from gtkui import UI + elif self.options['interface'] == "gt": + from gtktrayui import UI + else: + print "no GUI defined" + sys.exit() + + self.ui = UI(args, self.options['continuous']) + self.ui.connect("command", self.process_command) + # Can we load the icon resource? + icon = self.load_resource("icon.png") + if icon: + self.ui.set_icon_active_asset(icon) + # Can we load the icon_inactive resource? + icon_inactive = self.load_resource("icon_inactive.png") + if icon_inactive: + self.ui.set_icon_inactive_asset(icon_inactive) + + if self.options['history']: + self.history = [] + + # Create the recognizer + try: + self.recognizer = Recognizer(lang_file, dic_file, self.options['microphone']) + except Exception, e: + #no recognizer? bummer + print 'error making recognizer' + sys.exit() + + self.recognizer.connect('finished', self.recognizer_finished) + + print "Using Options: ", self.options + + def read_commands(self): + # Read the commands file + file_lines = open(command_file) + strings = open(strings_file, "w") + for line in file_lines: + print line + # Trim the white spaces + line = line.strip() + # If the line has length and the first char isn't a hash + if len(line) and line[0]!="#": + # This is a parsible line + (key,value) = line.split(":",1) + print key, value + self.commands[key.strip().lower()] = value.strip() + strings.write( key.strip()+"\n") + # Close the strings file + strings.close() + + def load_options(self): + # Is there an opt file? + try: + opt_fh = open(opt_file) + text = opt_fh.read() + self.options = yaml.load(text) + except: + pass + + + def log_history(self,text): + if self.options['history']: + self.history.append(text) + if len(self.history) > self.options['history']: + # Pop off the first item + self.history.pop(0) + + # Open and truncate the blather history file + hfile = open(history_file, "w") + for line in self.history: + hfile.write( line+"\n") + # Close the file + hfile.close() + + def run_command(self, cmd): + '''Print the command, then run it''' + print cmd + subprocess.call(cmd, shell=True) + + def recognizer_finished(self, recognizer, text): + t = text.lower() + # Is there a matching command? + if self.commands.has_key( t ): + # Run the valid_sentence_command if there is a valid sentence command + if self.options['valid_sentence_command']: + subprocess.call(self.options['valid_sentence_command'], shell=True) + cmd = self.commands[t] + # Should we be passing words? + if self.options['pass_words']: + cmd += " " + t + self.run_command(cmd) + else: + self.run_command(cmd) + self.log_history(text) + else: + # Run the invalid_sentence_command if there is an invalid sentence command + if self.options['invalid_sentence_command']: + subprocess.call(self.options['invalid_sentence_command'], shell=True) + print "no matching command %s" % t + # If there is a UI and we are not continuous listen + if self.ui: + if not self.continuous_listen: + # Stop listening + self.recognizer.pause() + # Let the UI know that there is a finish + self.ui.finished(t) + + def run(self): + if self.ui: + self.ui.run() + else: + blather.recognizer.listen() + + def quit(self): + sys.exit() + + def process_command(self, UI, command): + print command + if command == "listen": + self.recognizer.listen() + elif command == "stop": + self.recognizer.pause() + elif command == "continuous_listen": + self.continuous_listen = True + self.recognizer.listen() + elif command == "continuous_stop": + self.continuous_listen = False + self.recognizer.pause() + elif command == "quit": + self.quit() + + def load_resource(self,string): + local_data = os.path.join(os.path.dirname(__file__), 'data') + paths = ["/usr/share/blather/","/usr/local/share/blather", local_data] + for path in paths: + resource = os.path.join(path, string) + if os.path.exists( resource ): + return resource + # If we get this far, no resource was found + return False + + +if __name__ == "__main__": + parser = OptionParser() + parser.add_option("-i", "--interface", type="string", dest="interface", + action='store', + help="Interface to use (if any). 'g' for GTK or 'gt' for GTK system tray icon") + + parser.add_option("-c", "--continuous", + action="store_true", dest="continuous", default=False, + help="starts interface with 'continuous' listen enabled") + + parser.add_option("-p", "--pass-words", + action="store_true", dest="pass_words", default=False, + help="passes the recognized words as arguments to the shell command") + + parser.add_option("-o", "--override", + action="store_true", dest="override", default=False, + help="override config file with command line options") + + parser.add_option("-H", "--history", type="int", + action="store", dest="history", + help="number of commands to store in history file") + + parser.add_option("-m", "--microphone", type="int", + action="store", dest="microphone", default=None, + help="Audio input card to use (if other than system default)") + + parser.add_option("--valid-sentence-command", type="string", dest="valid_sentence_command", + action='store', + help="command to run when a valid sentence is detected") + + parser.add_option( "--invalid-sentence-command", type="string", dest="invalid_sentence_command", + action='store', + help="command to run when an invalid sentence is detected") + + (options, args) = parser.parse_args() + # Make our blather object + blather = Blather(options) + # Init gobject threads + GObject.threads_init() + # We want a main loop + main_loop = GObject.MainLoop() + # Handle sigint + signal.signal(signal.SIGINT, signal.SIG_DFL) + # Run the blather + blather.run() + # Start the main loop + try: + main_loop.run() + except: + print "time to quit" + main_loop.quit() + sys.exit() + diff --git a/gtktrayui.py b/gtktrayui.py new file mode 100644 index 0000000..cb66259 --- /dev/null +++ b/gtktrayui.py @@ -0,0 +1,105 @@ +# This is part of Kaylee +# -- this code is licensed GPLv3 +# Copyright 2013 Jezra +# Copyright 2015 Clayton G. Hobbs + +import sys +from gi.repository import GObject +# Gtk +from gi.repository import Gtk, Gdk + +class UI(GObject.GObject): + __gsignals__ = { + 'command' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE, (GObject.TYPE_STRING,)) + } + idle_text = "Kaylee - Idle" + listening_text = "Kaylee - Listening" + + def __init__(self, args, continuous): + GObject.GObject.__init__(self) + self.continuous = continuous + + self.statusicon = Gtk.StatusIcon() + self.statusicon.set_title("Kaylee") + self.statusicon.set_name("Kaylee") + self.statusicon.set_tooltip_text(self.idle_text) + self.statusicon.set_has_tooltip(True) + self.statusicon.connect("activate", self.continuous_toggle) + self.statusicon.connect("popup-menu", self.popup_menu) + + self.menu = Gtk.Menu() + self.menu_listen = Gtk.MenuItem('Listen') + self.menu_continuous = Gtk.CheckMenuItem('Continuous') + self.menu_quit = Gtk.MenuItem('Quit') + self.menu.append(self.menu_listen) + self.menu.append(self.menu_continuous) + self.menu.append(self.menu_quit) + self.menu_listen.connect("activate", self.toggle_listen) + self.menu_continuous.connect("toggled", self.toggle_continuous) + self.menu_quit.connect("activate", self.quit) + self.menu.show_all() + + def continuous_toggle(self, item): + checked = self.menu_continuous.get_active() + self.menu_continuous.set_active(not checked) + + def toggle_continuous(self, item): + checked = self.menu_continuous.get_active() + self.menu_listen.set_sensitive(not checked) + if checked: + self.menu_listen.set_label("Listen") + self.emit('command', "continuous_listen") + self.statusicon.set_tooltip_text(self.listening_text) + self.set_icon_active() + else: + self.set_icon_inactive() + self.statusicon.set_tooltip_text(self.idle_text) + self.emit('command', "continuous_stop") + + def toggle_listen(self, item): + val = self.menu_listen.get_label() + if val == "Listen": + self.set_icon_active() + self.emit("command", "listen") + self.menu_listen.set_label("Stop") + self.statusicon.set_tooltip_text(self.listening_text) + else: + self.set_icon_inactive() + self.menu_listen.set_label("Listen") + self.emit("command", "stop") + self.statusicon.set_tooltip_text(self.idle_text) + + def popup_menu(self, item, button, time): + self.menu.popup(None, None, Gtk.StatusIcon.position_menu, item, button, time) + + def run(self): + # Set the icon + self.set_icon_inactive() + if self.continuous: + self.menu_continuous.set_active(True) + self.set_icon_active() + else: + self.menu_continuous.set_active(False) + self.statusicon.set_visible(True) + + def quit(self, item): + self.statusicon.set_visible(False) + self.emit("command", "quit") + + def finished(self, text): + if not self.menu_continuous.get_active(): + self.menu_listen.set_label("Listen") + self.set_icon_inactive() + self.statusicon.set_tooltip_text(self.idle_text) + + def set_icon_active_asset(self, i): + self.icon_active = i + + def set_icon_inactive_asset(self, i): + self.icon_inactive = i + + def set_icon_active(self): + self.statusicon.set_from_file(self.icon_active) + + def set_icon_inactive(self): + self.statusicon.set_from_file(self.icon_inactive) diff --git a/gtkui.py b/gtkui.py new file mode 100644 index 0000000..7c3600d --- /dev/null +++ b/gtkui.py @@ -0,0 +1,111 @@ +# This is part of Kaylee +# -- this code is licensed GPLv3 +# Copyright 2013 Jezra +# Copyright 2015 Clayton G. Hobbs + +import sys +from gi.repository import GObject +# Gtk +from gi.repository import Gtk, Gdk + +class UI(GObject.GObject): + __gsignals__ = { + 'command' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE, (GObject.TYPE_STRING,)) + } + + def __init__(self,args, continuous): + GObject.GObject.__init__(self) + self.continuous = continuous + # Make a window + self.window = Gtk.Window(Gtk.WindowType.TOPLEVEL) + self.window.connect("delete_event", self.delete_event) + # Give the window a name + self.window.set_title("Kaylee") + self.window.set_resizable(False) + + layout = Gtk.VBox() + self.window.add(layout) + # Make a listen/stop button + self.lsbutton = Gtk.Button("Listen") + layout.add(self.lsbutton) + # Make a continuous button + self.ccheckbox = Gtk.CheckButton("Continuous Listen") + layout.add(self.ccheckbox) + + # Connect the buttons + self.lsbutton.connect("clicked",self.lsbutton_clicked) + self.ccheckbox.connect("clicked",self.ccheckbox_clicked) + + # Add a label to the UI to display the last command + self.label = Gtk.Label() + layout.add(self.label) + + # Create an accellerator group for this window + accel = Gtk.AccelGroup() + # Add the ctrl+q to quit + accel.connect(Gdk.keyval_from_name('q'), Gdk.ModifierType.CONTROL_MASK, + Gtk.AccelFlags.VISIBLE, self.accel_quit) + # Lock the group + accel.lock() + # Add the group to the window + self.window.add_accel_group(accel) + + def ccheckbox_clicked(self, widget): + checked = self.ccheckbox.get_active() + self.lsbutton.set_sensitive(not checked) + if checked: + self.lsbutton_stopped() + self.emit('command', "continuous_listen") + self.set_icon_active() + else: + self.emit('command', "continuous_stop") + self.set_icon_inactive() + + def lsbutton_stopped(self): + self.lsbutton.set_label("Listen") + + def lsbutton_clicked(self, button): + val = self.lsbutton.get_label() + if val == "Listen": + self.emit("command", "listen") + self.lsbutton.set_label("Stop") + # Clear the label + self.label.set_text("") + self.set_icon_active() + else: + self.lsbutton_stopped() + self.emit("command", "stop") + self.set_icon_inactive() + + def run(self): + # Set the default icon + self.set_icon_inactive() + self.window.show_all() + if self.continuous: + self.set_icon_active() + self.ccheckbox.set_active(True) + + def accel_quit(self, accel_group, acceleratable, keyval, modifier): + self.emit("command", "quit") + + def delete_event(self, x, y): + self.emit("command", "quit") + + def finished(self, text): + # If the continuous isn't pressed + if not self.ccheckbox.get_active(): + self.lsbutton_stopped() + self.set_icon_inactive() + self.label.set_text(text) + + def set_icon_active_asset(self, i): + self.icon_active = i + + def set_icon_inactive_asset(self, i): + self.icon_inactive = i + + def set_icon_active(self): + Gtk.Window.set_default_icon_from_file(self.icon_active) + + def set_icon_inactive(self): + Gtk.Window.set_default_icon_from_file(self.icon_inactive) diff --git a/recognizer.py b/recognizer.py new file mode 100755 index 0000000..038d4cf --- /dev/null +++ b/recognizer.py @@ -0,0 +1,66 @@ +# This is part of Kaylee +# -- this code is licensed GPLv3 +# Copyright 2013 Jezra +# Copyright 2015 Clayton G. Hobbs + +import gi +gi.require_version('Gst', '1.0') +from gi.repository import GObject, Gst +GObject.threads_init() +Gst.init(None) +import os.path +import sys + +# Define some global variables +this_dir = os.path.dirname( os.path.abspath(__file__) ) + + +class Recognizer(GObject.GObject): + __gsignals__ = { + 'finished' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE, (GObject.TYPE_STRING,)) + } + + def __init__(self, language_file, dictionary_file, src = None): + GObject.GObject.__init__(self) + self.commands = {} + if src: + audio_src = 'alsasrc device="hw:%d,0"' % (src) + else: + audio_src = 'autoaudiosrc' + + # Build the pipeline + cmd = audio_src+' ! audioconvert ! audioresample ! pocketsphinx name=asr ! appsink sync=false' + try: + self.pipeline=Gst.parse_launch( cmd ) + except Exception, e: + print e.message + print "You may need to install gstreamer1.0-pocketsphinx" + raise e + + bus = self.pipeline.get_bus() + bus.add_signal_watch() + + # Get the Auto Speech Recognition piece + asr=self.pipeline.get_by_name('asr') + bus.connect('message::element', self.result) + asr.set_property('lm', language_file) + asr.set_property('dict', dictionary_file) + asr.set_property('configured', True) + + def listen(self): + self.pipeline.set_state(Gst.State.PLAYING) + + def pause(self): + self.pipeline.set_state(Gst.State.PAUSED) + + def result(self, bus, msg): + msg_struct = msg.get_structure() + # Ignore messages that aren't from pocketsphinx + msgtype = msg_struct.get_name() + if msgtype != 'pocketsphinx': + return + + # If we have a final command, send it for processing + command = msg_struct.get_string('hypothesis') + if command != '' and msg_struct.get_boolean('final')[1]: + self.emit("finished", command) -- cgit 1.4.1