Merge branch 'master' of gitlab.com:hugvey/hugvey

This commit is contained in:
Ruben van de Ven 2019-05-17 16:40:23 +02:00
commit d904facbe7

View file

@ -33,7 +33,7 @@ class GoogleVoiceClient(object):
self.hugvey = hugvey self.hugvey = hugvey
self.language_code = language_code self.language_code = language_code
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = credential_file os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = credential_file
# Create a thread-safe buffer of audio data # Create a thread-safe buffer of audio data
self.buffer = queue.Queue() self.buffer = queue.Queue()
self.isRunning = threading.Event() self.isRunning = threading.Event()
@ -41,53 +41,53 @@ class GoogleVoiceClient(object):
self.target_rate = 16000 self.target_rate = 16000
self.cv_laststate = None self.cv_laststate = None
self.restart = False self.restart = False
self.task = threading.Thread(target=self.run, name=f"hugvey#{self.hugvey.id}v") self.task = threading.Thread(target=self.run, name=f"hugvey#{self.hugvey.id}v")
self.task.setDaemon(True) self.task.setDaemon(True)
self.task.start() self.task.start()
self.subsequentMutedFrames = 0 self.subsequentMutedFrames = 0
self.lastNonFinalTranscript = None self.lastNonFinalTranscript = None
def pause(self): def pause(self):
self.isRunning.clear() self.isRunning.clear()
self.restart = True self.restart = True
def resume(self): def resume(self):
self.buffer = queue.Queue() # have a clear queue when resuming self.buffer = queue.Queue() # have a clear queue when resuming
self.isRunning.set() self.isRunning.set()
def generator(self): def generator(self):
self.logger.debug('start generator') self.logger.debug('start generator')
while not self.toBeShutdown and self.isRunning.is_set(): while not self.toBeShutdown and self.isRunning.is_set():
try: try:
# set a timeout, as not to wait infinitely for the buffer when # set a timeout, as not to wait infinitely for the buffer when
# we actually want to restart # we actually want to restart
yield self.buffer.get(timeout=.3) yield self.buffer.get(timeout=.3)
except queue.Empty as e: except queue.Empty as e:
self.logger.debug('empty mic buffer - restart?') self.logger.debug('empty mic buffer - restart?')
# print(self.isRunning.isSet()) # print(self.isRunning.isSet())
self.logger.info('stop generator') self.logger.info('stop generator')
self.restart = False # don't trigger double restart self.restart = False # don't trigger double restart
# raise RequireRestart("Restart required (generator)") # raise RequireRestart("Restart required (generator)")
def setLanguage(self, language_code): def setLanguage(self, language_code):
if self.language_code == language_code: if self.language_code == language_code:
return return
self.logger.info("Change language from {} to {}".format(self.language_code, language_code)) self.logger.info("Change language from {} to {}".format(self.language_code, language_code))
self.language_code = language_code self.language_code = language_code
self.isRunning.clear() self.isRunning.clear()
self.restart = True self.restart = True
def run(self): def run(self):
self.isRunning.set() self.isRunning.set()
# Leave this here to avoid "Too many files open" errors. # Leave this here to avoid "Too many files open" errors.
self.speech_client = speech.SpeechClient() self.speech_client = speech.SpeechClient()
while not self.toBeShutdown: while not self.toBeShutdown:
config = types.RecognitionConfig( config = types.RecognitionConfig(
encoding=enums.RecognitionConfig.AudioEncoding.LINEAR16, encoding=enums.RecognitionConfig.AudioEncoding.LINEAR16,
@ -96,59 +96,59 @@ class GoogleVoiceClient(object):
self.streaming_config = types.StreamingRecognitionConfig( self.streaming_config = types.StreamingRecognitionConfig(
config=config, config=config,
interim_results=True) interim_results=True)
try: try:
self.logger.log(LOG_BS,"wait for Google Voice") self.logger.log(LOG_BS,"wait for Google Voice")
if not self.isRunning.wait(timeout=1): if not self.isRunning.wait(timeout=1):
continue # re-ceck toBeShutdown continue # re-ceck toBeShutdown
self.logger.debug("Starting Google Voice") self.logger.debug("Starting Google Voice")
audio_generator = self.generator() audio_generator = self.generator()
requests = (types.StreamingRecognizeRequest(audio_content=content) requests = (types.StreamingRecognizeRequest(audio_content=content)
for content in audio_generator) for content in audio_generator)
responses = self.speech_client.streaming_recognize( responses = self.speech_client.streaming_recognize(
self.streaming_config, requests) self.streaming_config, requests)
self.logger.debug("Starting voice loop") self.logger.debug("Starting voice loop")
for response in responses: for response in responses:
if not response.results: if not response.results:
self.logger.debug('...') self.logger.debug('...')
continue continue
"""Iterates through server responses and prints them. """Iterates through server responses and prints them.
The responses passed is a generator that will block until a response The responses passed is a generator that will block until a response
is provided by the server. is provided by the server.
Each response may contain multiple results, and each result may contain Each response may contain multiple results, and each result may contain
multiple alternatives; for details, see https://goo.gl/tjCPAU. Here we multiple alternatives; for details, see https://goo.gl/tjCPAU. Here we
print only the transcription for the top alternative of the top result. print only the transcription for the top alternative of the top result.
In this case, responses are provided for interim results as well. If the In this case, responses are provided for interim results as well. If the
response is an interim one, print a line feed at the end of it, to allow response is an interim one, print a line feed at the end of it, to allow
the next result to overwrite it, until the response is a final one. For the the next result to overwrite it, until the response is a final one. For the
final one, print a newline to preserve the finalized transcription. final one, print a newline to preserve the finalized transcription.
""" """
# The `results` list is consecutive. For streaming, we only care about # The `results` list is consecutive. For streaming, we only care about
# the first result being considered, since once it's `is_final`, it # the first result being considered, since once it's `is_final`, it
# moves on to considering the next utterance. # moves on to considering the next utterance.
result = response.results[0] result = response.results[0]
if not result.alternatives: if not result.alternatives:
continue continue
# Display the transcription of the top alternative. # Display the transcription of the top alternative.
transcript = result.alternatives[0].transcript transcript = result.alternatives[0].transcript
# self.logger.debug("Text: ".format(transcript)) # self.logger.debug("Text: ".format(transcript))
if not result.is_final: if not result.is_final:
self.logger.debug(f"Text: {transcript}") self.logger.debug(f"Text: {transcript}")
self.lastNonFinalTranscript = transcript self.lastNonFinalTranscript = transcript
else: else:
self.logger.info(f"Text: {transcript}") self.logger.info(f"Text: {transcript}")
self.lastNonFinalTranscript = None self.lastNonFinalTranscript = None
if result.is_final: if result.is_final:
self.logger.info("native final") self.logger.info("native final")
msg = { msg = {
@ -156,13 +156,13 @@ class GoogleVoiceClient(object):
"is_final": result.is_final, "is_final": result.is_final,
"transcript": transcript.strip(), "transcript": transcript.strip(),
} }
self.hugvey.queueEvent(msg) self.hugvey.queueEvent(msg)
if self.restart: if self.restart:
self.restart = False self.restart = False
raise RequireRestart("Restart required") raise RequireRestart("Restart required")
if self.toBeShutdown: if self.toBeShutdown:
self.logger.warn("Stopping voice loop") self.logger.warn("Stopping voice loop")
break break
@ -170,30 +170,33 @@ class GoogleVoiceClient(object):
self.restart = False self.restart = False
self.logger.warn("Restart Google Voice. Language: {}".format(self.language_code)) self.logger.warn("Restart Google Voice. Language: {}".format(self.language_code))
except Exception as e: except Exception as e:
self.logger.critical(f"Crashed Google Voice: {e}") if "305" in str(e):
self.logger.warning(f"Long Google Voice: {e}")
else:
self.logger.critical(f"Crashed Google Voice: {e}")
# sending an extra message is deprecated since we ignore finals anyway # sending an extra message is deprecated since we ignore finals anyway
# make sure we always send a 'final' transcript. # make sure we always send a 'final' transcript.
# if self.lastNonFinalTranscript is not None: # if self.lastNonFinalTranscript is not None:
# msg = { # msg = {
# "event": "speech", # "event": "speech",
# "is_final": True, # "is_final": True,
# "transcript": self.lastNonFinalTranscript.strip(), # "transcript": self.lastNonFinalTranscript.strip(),
# } # }
# self.hugvey.queueEvent(msg) # self.hugvey.queueEvent(msg)
self.logger.warn("Stop google run()") # finish means wrapping of hugvey#3v thread self.logger.warn("Stop google run()") # finish means wrapping of hugvey#3v thread
# time.sleep(1) # time.sleep(1)
# for i in gc.get_referrers(self): # for i in gc.get_referrers(self):
# print(i) # print(i)
def receive(self, chunk): def receive(self, chunk):
if not self.task.isAlive(): if not self.task.isAlive():
raise Exception("Voice thread died") raise Exception("Voice thread died")
if audioop.max(chunk, 2) == 0: if audioop.max(chunk, 2) == 0:
# mic is muted on client side. # mic is muted on client side.
self.subsequentMutedFrames += 1 self.subsequentMutedFrames += 1
@ -202,31 +205,30 @@ class GoogleVoiceClient(object):
self.logger.info("Pause muted stream!") self.logger.info("Pause muted stream!")
self.pause() self.pause()
return return
self.subsequentMutedFrames = 0 self.subsequentMutedFrames = 0
# self.logger.debug("We have mic!") # self.logger.debug("We have mic!")
if not self.isRunning.is_set(): if not self.isRunning.is_set():
self.logger.info("Resume voice") self.logger.info("Resume voice")
self.resume() self.resume()
if not self.isRunning.is_set(): if not self.isRunning.is_set():
# logger.log(LOG_BS, "Don't put to queue if google is paused") # logger.log(LOG_BS, "Don't put to queue if google is paused")
return return
if self.src_rate == self.target_rate: if self.src_rate == self.target_rate:
data = chunk data = chunk
else: else:
data, self.cv_laststate = audioop.ratecv(chunk, 2, 1, self.src_rate, self.target_rate, self.cv_laststate) data, self.cv_laststate = audioop.ratecv(chunk, 2, 1, self.src_rate, self.target_rate, self.cv_laststate)
self.buffer.put_nowait(data) self.buffer.put_nowait(data)
def shutdown(self): def shutdown(self):
self.toBeShutdown = True self.toBeShutdown = True
self.hugvey = None self.hugvey = None
def triggerStart(self): def triggerStart(self):
pass pass
def __del__(self): def __del__(self):
self.logger.warn("Destroyed google object") self.logger.warn("Destroyed google object")