NeuronModule.py 5.9 KB

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