NeuronModule.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. # coding: utf8
  2. import logging
  3. import os
  4. import random
  5. import sys
  6. from jinja2 import Template
  7. from core import OrderListener
  8. from core.SynapseLauncher import SynapseLauncher
  9. from core.Utils import Utils
  10. from core.ConfigurationManager import SettingLoader, BrainLoader
  11. logging.basicConfig()
  12. logger = logging.getLogger("kalliope")
  13. class InvalidParameterException(Exception):
  14. pass
  15. class MissingParameterException(Exception):
  16. pass
  17. class NoTemplateException(Exception):
  18. pass
  19. class MultipleTemplateException(Exception):
  20. pass
  21. class TemplateFileNotFoundException(Exception):
  22. pass
  23. class TTSModuleNotFound(Exception):
  24. pass
  25. class TTSNotInstantiable(Exception):
  26. pass
  27. class NeuronModule(object):
  28. def __init__(self, **kwargs):
  29. """
  30. Class used by neuron for talking
  31. :param kwargs: Same parameter as the Child. Can contain info about the tts to use instead of the
  32. default one
  33. """
  34. # get the child who called the class
  35. child_name = self.__class__.__name__
  36. logger.debug("NeuronModule called from class %s with parameters: %s" % (child_name, str(kwargs)))
  37. self.settings = SettingLoader.get_settings()
  38. self.brain = BrainLoader.get_brain()
  39. # check if the user has overrider the TTS
  40. tts = kwargs.get('tts', None)
  41. if tts is None:
  42. # No tts provided, we load the default one
  43. self.tts = self.settings.default_tts_name
  44. else:
  45. self.tts = tts
  46. # get if the cache settings is present
  47. self.override_cache = kwargs.get('cache', None)
  48. # get templates if provided
  49. # Check if there is a template associate to the output message
  50. self.say_template = kwargs.get('say_template', None)
  51. # check if there is a template file associate to the output message
  52. self.file_template = kwargs.get('file_template', None)
  53. def say(self, message):
  54. """
  55. USe TTS to speak out loud the Message.
  56. A message can be a string, a list or a dict
  57. If it's a string, simply use the TTS with the message
  58. If it's a list, we select randomly a string in the list and give it to the TTS
  59. If it's a dict, we use the template given in parameter to create a string that we give to the TTS
  60. :param message: Can be a String or a dict
  61. :return:
  62. """
  63. logger.debug("NeuronModule Say() called with message: %s" % message)
  64. tts_message = None
  65. if isinstance(message, str) or isinstance(message, unicode):
  66. logger.debug("message is string")
  67. tts_message = message
  68. if isinstance(message, list):
  69. logger.debug("message is list")
  70. tts_message = random.choice(message)
  71. if isinstance(message, dict):
  72. logger.debug("message is dict")
  73. tts_message = self._get_message_from_dict(message)
  74. if tts_message is not None:
  75. logger.debug("tts_message to say: %s" % tts_message)
  76. # create a tts object from the tts the user want to user
  77. tts_object = next((x for x in self.settings.ttss if x.name == self.tts), None)
  78. if tts_object is None:
  79. raise TTSModuleNotFound("The tts module name %s does not exist in settings file" % self.tts)
  80. # change the cache settings with the one precised for the current neuron
  81. if self.override_cache is not None:
  82. tts_object.parameter = self._update_cache_var(self.override_cache, tts_object.parameter)
  83. logger.debug("NeuroneModule: TTS args: %s" % tts_object)
  84. # get the instance of the TTS module
  85. tts_module_instance = Utils.get_dynamic_class_instantiation("tts", tts_object.name.capitalize(),
  86. tts_object.parameters)
  87. # generate the audio file and play it
  88. tts_module_instance.say(tts_message)
  89. def _get_message_from_dict(self, message_dict):
  90. """
  91. Generate a message taht can be played by a TTS engine from a dict of variable and the jinja template
  92. :param message_dict:
  93. :return:
  94. """
  95. returned_message = None
  96. if (self.say_template is not None and self.file_template is None) or \
  97. (self.say_template is None and self.file_template is not None):
  98. # the user choose a say_template option
  99. if self.say_template is not None:
  100. if isinstance(self.say_template, list):
  101. # then we pick randomly one template
  102. self.say_template = random.choice(self.say_template)
  103. t = Template(self.say_template)
  104. returned_message = t.render(**message_dict)
  105. # trick to remobe unicode problem when loading jinja template with non ascii char
  106. reload(sys)
  107. sys.setdefaultencoding('utf-8')
  108. # the user choose a file_template option
  109. if self.file_template is not None: # the user choose a file_template option
  110. real_file_template_path = "templates/%s" % self.file_template
  111. if os.path.isfile(real_file_template_path):
  112. # load the content of the file as template
  113. t = Template(self._get_content_of_file(real_file_template_path))
  114. returned_message = t.render(**message_dict)
  115. else:
  116. raise TemplateFileNotFoundException("Template file %s not found in templates folder"
  117. % real_file_template_path)
  118. return returned_message
  119. # we don't force the usage of a template. The user can choose to do nothing with returned value
  120. # else:
  121. # raise NoTemplateException("You must specify a say_template or a file_template")
  122. def run_synapse_ny_name(self, name):
  123. SynapseLauncher.start_synapse(name=name, brain=self.brain)
  124. @staticmethod
  125. def _get_content_of_file(real_file_template_path):
  126. with open(real_file_template_path, 'r') as content_file:
  127. return content_file.read()
  128. @staticmethod
  129. def _update_cache_var(new_override_cache, args_list):
  130. logger.debug("args for TTS plugin before update: %s" % str(args_list))
  131. args_list["cache"] = new_override_cache
  132. logger.debug("args for TTS plugin after update: %s" % str(args_list))
  133. return args_list
  134. @staticmethod
  135. def get_audio_from_stt(callback):
  136. """
  137. Call the default STT to get an audio sample and return it into the callback method
  138. :param callback:
  139. :return:
  140. """
  141. # call the order listener
  142. oa = OrderListener(callback=callback)
  143. oa.start()