Somewhat fancy voice command recognition software
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

kaylee.py 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. # This is part of Kaylee
  2. # -- this code is licensed GPLv3
  3. # Copyright 2015-2016 Clayton G. Hobbs
  4. # Portions Copyright 2013 Jezra
  5. import importlib
  6. import sys
  7. import signal
  8. import os.path
  9. from gi.repository import GObject, GLib
  10. from kayleevc.recognizer import Recognizer
  11. from kayleevc.util import *
  12. from kayleevc.numbers import NumberParser
  13. import kayleevc.plugins
  14. class Kaylee:
  15. def __init__(self):
  16. self.ui = None
  17. self.options = {}
  18. self.continuous_listen = False
  19. # Load configuration
  20. self.config = Config()
  21. self.options = vars(self.config.options)
  22. self.commands = self.options['plugins']['.shell']
  23. # Load plugins
  24. self.plugins = []
  25. for plugin in self.options['plugins'].keys():
  26. pmod = importlib.import_module(plugin, 'kayleevc.plugins')
  27. self.plugins.append(pmod.Plugin(self.config))
  28. # Create a hasher
  29. self.hasher = Hasher(self.config)
  30. # Create the strings file
  31. self.update_voice_commands_if_changed()
  32. if self.options['interface']:
  33. if self.options['interface'] == "g":
  34. from kayleevc.gui import GTKInterface as UI
  35. elif self.options['interface'] == "gt":
  36. from kayleevc.gui import GTKTrayInterface as UI
  37. else:
  38. print("no GUI defined")
  39. sys.exit()
  40. self.ui = UI(self.options, self.options['continuous'])
  41. self.ui.connect("command", self.process_command)
  42. # Can we load the icon resource?
  43. icon = self.load_resource("icon_small.png")
  44. if icon:
  45. self.ui.set_icon_active_asset(icon)
  46. # Can we load the icon_inactive resource?
  47. icon_inactive = self.load_resource("icon_inactive_small.png")
  48. if icon_inactive:
  49. self.ui.set_icon_inactive_asset(icon_inactive)
  50. if self.options['history']:
  51. self.history = []
  52. # Update the language if necessary
  53. self.language_updater = LanguageUpdater(self.config)
  54. self.language_updater.update_language_if_changed()
  55. # Create the recognizer
  56. self.recognizer = Recognizer(self.config)
  57. # Connect the recognizer's finished signal to all the plugins
  58. for plugin in self.plugins:
  59. self.recognizer.connect('finished', plugin.recognizer_finished)
  60. def update_voice_commands_if_changed(self):
  61. """Use hashes to test if the voice commands have changed"""
  62. stored_hash = self.hasher['voice_commands']
  63. # Calculate the hash the voice commands have right now
  64. hasher = self.hasher.get_hash_object()
  65. for plugin in self.plugins:
  66. for string in sorted(plugin.corpus_strings):
  67. hasher.update(string.encode('utf-8'))
  68. # Add a separator to avoid odd behavior
  69. hasher.update('\n'.encode('utf-8'))
  70. new_hash = hasher.hexdigest()
  71. if new_hash != stored_hash:
  72. self.create_strings_file()
  73. self.hasher['voice_commands'] = new_hash
  74. self.hasher.store()
  75. def create_strings_file(self):
  76. # Open the strings file
  77. with open(self.config.strings_file, 'w') as strings:
  78. # Add command words to the corpus
  79. # FIXME: Doing this twice is a silly thing
  80. for plugin in self.plugins:
  81. for string in sorted(plugin.corpus_strings):
  82. strings.write(string + "\n")
  83. # Add number words to the corpus
  84. if NumberParser.number_words is not None:
  85. for word in NumberParser.number_words:
  86. strings.write(word + " ")
  87. strings.write("\n")
  88. def log_history(self, text):
  89. if self.options['history']:
  90. self.history.append(text)
  91. if len(self.history) > self.options['history']:
  92. # Pop off the first item
  93. self.history.pop(0)
  94. # Open and truncate the history file
  95. with open(self.config.history_file, 'w') as hfile:
  96. for line in self.history:
  97. hfile.write(line + '\n')
  98. def recognizer_finished(self, recognizer, text):
  99. pass
  100. def run(self):
  101. if self.ui:
  102. self.ui.run()
  103. else:
  104. self.recognizer.listen()
  105. def quit(self):
  106. sys.exit()
  107. def process_command(self, UI, command):
  108. print(command)
  109. if command == "listen":
  110. self.recognizer.listen()
  111. elif command == "stop":
  112. self.recognizer.pause()
  113. elif command == "continuous_listen":
  114. self.continuous_listen = True
  115. self.recognizer.listen()
  116. elif command == "continuous_stop":
  117. self.continuous_listen = False
  118. self.recognizer.pause()
  119. elif command == "quit":
  120. self.quit()
  121. def load_resource(self, string):
  122. # TODO: Use the Config object for this path management
  123. local_data = os.path.join(os.path.dirname(__file__), '..', 'data')
  124. paths = ["/usr/share/kaylee/", "/usr/local/share/kaylee", local_data]
  125. for path in paths:
  126. resource = os.path.join(path, string)
  127. if os.path.exists(resource):
  128. return resource
  129. # If we get this far, no resource was found
  130. return False
  131. def run():
  132. # Make our kaylee object
  133. kaylee = Kaylee()
  134. # Init gobject threads
  135. GObject.threads_init()
  136. # We want a main loop
  137. main_loop = GObject.MainLoop()
  138. # Handle sigint
  139. signal.signal(signal.SIGINT, signal.SIG_DFL)
  140. # Run the kaylee
  141. kaylee.run()
  142. # Start the main loop
  143. try:
  144. main_loop.run()
  145. except:
  146. main_loop.quit()
  147. sys.exit()