speech.py
Go to the documentation of this file.
1 # Copyright 2017 Mycroft AI Inc.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 #
15 import re
16 import time
17 from threading import Lock
18 
19 from mycroft.configuration import Configuration
20 from mycroft.metrics import report_timing, Stopwatch
21 from mycroft.tts import TTSFactory
22 from mycroft.util import create_signal, check_for_signal
23 from mycroft.util.log import LOG
24 from mycroft.messagebus.message import Message
25 from mycroft.tts.remote_tts import RemoteTTSTimeoutException
26 from mycroft.tts.mimic_tts import Mimic
27 
28 bus = None # Mycroft messagebus connection
29 config = None
30 tts = None
31 tts_hash = None
32 lock = Lock()
33 mimic_fallback_obj = None
34 
35 _last_stop_signal = 0
36 
37 
38 def _start_listener(message):
39  """
40  Force Mycroft to start listening (as if 'Hey Mycroft' was spoken)
41  """
42  create_signal('startListening')
43 
44 
45 def handle_speak(event):
46  """
47  Handle "speak" message
48  """
49  config = Configuration.get()
50  Configuration.init(bus)
51  global _last_stop_signal
52 
53  # Get conversation ID
54  if event.context and 'ident' in event.context:
55  ident = event.context['ident']
56  else:
57  ident = 'unknown'
58 
59  start = time.time() # Time of speech request
60  with lock:
61  stopwatch = Stopwatch()
62  stopwatch.start()
63  utterance = event.data['utterance']
64  if event.data.get('expect_response', False):
65  # When expect_response is requested, the listener will be restarted
66  # at the end of the next bit of spoken audio.
67  bus.once('recognizer_loop:audio_output_end', _start_listener)
68 
69  # This is a bit of a hack for Picroft. The analog audio on a Pi blocks
70  # for 30 seconds fairly often, so we don't want to break on periods
71  # (decreasing the chance of encountering the block). But we will
72  # keep the split for non-Picroft installs since it give user feedback
73  # faster on longer phrases.
74  #
75  # TODO: Remove or make an option? This is really a hack, anyway,
76  # so we likely will want to get rid of this when not running on Mimic
77  if (config.get('enclosure', {}).get('platform') != "picroft" and
78  len(re.findall('<[^>]*>', utterance)) == 0):
79  # Remove any whitespace present after the period,
80  # if a character (only alpha) ends with a period
81  # ex: A. Lincoln -> A.Lincoln
82  # so that we don't split at the period
83  utterance = re.sub(r'\b([A-za-z][\.])(\s+)', r'\g<1>', utterance)
84  chunks = re.split(r'(?<!\w\.\w.)(?<![A-Z][a-z]\.)(?<=\.|\;|\?)\s',
85  utterance)
86  for chunk in chunks:
87  # Check if somthing has aborted the speech
88  if (_last_stop_signal > start or
89  check_for_signal('buttonPress')):
90  # Clear any newly queued speech
91  tts.playback.clear()
92  break
93  try:
94  mute_and_speak(chunk, ident)
95  except KeyboardInterrupt:
96  raise
97  except Exception:
98  LOG.error('Error in mute_and_speak', exc_info=True)
99  else:
100  mute_and_speak(utterance, ident)
101 
102  stopwatch.stop()
103  report_timing(ident, 'speech', stopwatch, {'utterance': utterance,
104  'tts': tts.__class__.__name__})
105 
106 
107 def mute_and_speak(utterance, ident):
108  """
109  Mute mic and start speaking the utterance using selected tts backend.
110 
111  Args:
112  utterance: The sentence to be spoken
113  ident: Ident tying the utterance to the source query
114  """
115  global tts_hash
116 
117  # update TTS object if configuration has changed
118  if tts_hash != hash(str(config.get('tts', ''))):
119  global tts
120  # Stop tts playback thread
121  tts.playback.stop()
122  tts.playback.join()
123  # Create new tts instance
124  tts = TTSFactory.create()
125  tts.init(bus)
126  tts_hash = hash(str(config.get('tts', '')))
127 
128  LOG.info("Speak: " + utterance)
129  try:
130  tts.execute(utterance, ident)
131  except RemoteTTSTimeoutException as e:
132  LOG.error(e)
133  mimic_fallback_tts(utterance, ident)
134  except Exception as e:
135  LOG.error('TTS execution failed ({})'.format(repr(e)))
136 
137 
138 def mimic_fallback_tts(utterance, ident):
139  global mimic_fallback_obj
140  # fallback if connection is lost
141  config = Configuration.get()
142  tts_config = config.get('tts', {}).get("mimic", {})
143  lang = config.get("lang", "en-us")
144  if not mimic_fallback_obj:
145  mimic_fallback_obj = Mimic(lang, tts_config)
146  tts = mimic_fallback_obj
147  LOG.debug("Mimic fallback, utterance : " + str(utterance))
148  tts.init(bus)
149  tts.execute(utterance, ident)
150 
151 
152 def handle_stop(event):
153  """
154  handle stop message
155  """
156  global _last_stop_signal
157  if check_for_signal("isSpeaking", -1):
158  _last_stop_signal = time.time()
159  tts.playback.clear() # Clear here to get instant stop
160  bus.emit(Message("mycroft.stop.handled", {"by": "TTS"}))
161 
162 
163 def init(messagebus):
164  """ Start speech related handlers.
165 
166  Arguments:
167  messagebus: Connection to the Mycroft messagebus
168  """
169 
170  global bus
171  global tts
172  global tts_hash
173  global config
174 
175  bus = messagebus
176  Configuration.init(bus)
177  config = Configuration.get()
178  bus.on('mycroft.stop', handle_stop)
179  bus.on('mycroft.audio.speech.stop', handle_stop)
180  bus.on('speak', handle_speak)
181  bus.on('mycroft.mic.listen', _start_listener)
182 
183  tts = TTSFactory.create()
184  tts.init(bus)
185  tts_hash = config.get('tts')
186 
187 
188 def shutdown():
189  if tts:
190  tts.playback.stop()
191  tts.playback.join()
192  if mimic_fallback_obj:
193  mimic_fallback_obj.playback.stop()
194  mimic_fallback_obj.playback.join()
def report_timing(ident, system, timing, additional_data=None)
def handle_stop(event)
Definition: speech.py:152
def check_for_signal(signal_name, sec_lifetime=0)
Definition: signal.py:105
def mimic_fallback_tts(utterance, ident)
Definition: speech.py:138
def init(messagebus)
Definition: speech.py:163
def create_signal(signal_name)
Definition: signal.py:90
def handle_speak(event)
Definition: speech.py:45
def mute_and_speak(utterance, ident)
Definition: speech.py:107
def get(phrase, lang=None, context=None)
def _start_listener(message)
Definition: speech.py:38


mycroft_ros
Author(s):
autogenerated on Mon Apr 26 2021 02:35:40