Support for multiple hotwords, configurations and assistant languages in Snowboy backend
This commit is contained in:
parent
6769707580
commit
8d660f27d4
2 changed files with 74 additions and 32 deletions
|
@ -50,30 +50,30 @@ class AssistantSnowboyBackend(Backend):
|
||||||
or train/download other models from https://snowboy.kitt.ai.
|
or train/download other models from https://snowboy.kitt.ai.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, voice_model_file, hotword=None, sensitivity=0.5,
|
def __init__(self, models, audio_gain=1.0, **kwargs):
|
||||||
audio_gain=1.0, assistant_plugin=None, **kwargs):
|
|
||||||
"""
|
"""
|
||||||
:param voice_model_file: Snowboy voice model file - \
|
:param models: Map (name -> configuration) of voice models to be used by
|
||||||
see https://snowboy.kitt.ai/
|
the assistant. See https://snowboy.kitt.ai/ for training/downloading
|
||||||
:type voice_model_file: str
|
models. Sample format::
|
||||||
|
|
||||||
:param hotword: Name of the hotword
|
ok_google: # Hotword model name
|
||||||
:type hotword: str
|
voice_model_file: /path/models/OK Google.pmdl # Voice model file location
|
||||||
|
sensitivity: 0.5 # Model sensitivity, between 0 and 1 (default: 0.5)
|
||||||
|
assistant_plugin: assistant.google.pushtotalk # When the hotword is detected trigger the Google push-to-talk assistant plugin (optional)
|
||||||
|
assistant_language: en-US # The assistant will conversate in English when this hotword is detected (optional)
|
||||||
|
detect_sound: /path/to/bell.wav # Sound file to be played when the hotword is detected (optional)
|
||||||
|
|
||||||
:param sensitivity: Hotword recognition sensitivity, between 0 and 1.
|
ciao_google: # Hotword model name
|
||||||
Default: 0.5.
|
voice_model_file: /path/models/Ciao Google.pmdl # Voice model file location
|
||||||
:type sensitivity: float
|
sensitivity: 0.5 # Model sensitivity, between 0 and 1 (default: 0.5)
|
||||||
|
assistant_plugin: assistant.google.pushtotalk # When the hotword is detected trigger the Google push-to-talk assistant plugin (optional)
|
||||||
|
assistant_language: it-IT # The assistant will conversate in Italian when this hotword is detected (optional)
|
||||||
|
detect_sound: /path/to/bell.wav # Sound file to be played when the hotword is detected (optional)
|
||||||
|
|
||||||
:param audio_gain: Audio gain, between 0 and 1
|
:type models: dict
|
||||||
|
|
||||||
|
:param audio_gain: Audio gain, between 0 and 1. Default: 1
|
||||||
:type audio_gain: float
|
:type audio_gain: float
|
||||||
|
|
||||||
:param assistant_plugin: By default Snowboy fires a
|
|
||||||
:class:`platypush.message.event.assistant.HotwordDetectedEvent` event
|
|
||||||
whenever the hotword is detected. You can also pass the plugin name of
|
|
||||||
a :class:`platypush.plugins.assistant.AssistantPlugin` instance
|
|
||||||
(for example ``assistant.google.pushtotalk``). If set, then the
|
|
||||||
assistant plugin will be invoked to start a conversation.
|
|
||||||
:type assistant_plugin: str
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -82,29 +82,68 @@ class AssistantSnowboyBackend(Backend):
|
||||||
import snowboy.snowboydecoder as snowboydecoder
|
import snowboy.snowboydecoder as snowboydecoder
|
||||||
|
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.voice_model_file = os.path.abspath(os.path.expanduser(voice_model_file))
|
|
||||||
self.hotword = hotword
|
self.models = {}
|
||||||
self.sensitivity = sensitivity
|
self._init_models(models)
|
||||||
self.audio_gain = audio_gain
|
self.audio_gain = audio_gain
|
||||||
self.assistant_plugin = assistant_plugin
|
|
||||||
|
|
||||||
self.detector = snowboydecoder.HotwordDetector(
|
self.detector = snowboydecoder.HotwordDetector(
|
||||||
self.voice_model_file, sensitivity=self.sensitivity,
|
[model['voice_model_file'] for model in self.models.values()],
|
||||||
|
sensitivity=[model['sensitivity'] for model in self.models.values()],
|
||||||
audio_gain=self.audio_gain)
|
audio_gain=self.audio_gain)
|
||||||
|
|
||||||
self.logger.info('Initialized Snowboy hotword detection')
|
self.logger.info('Initialized Snowboy hotword detection with {} voice model configurations'.format(len(self.models)))
|
||||||
|
|
||||||
def hotword_detected(self):
|
def _init_models(self, models):
|
||||||
|
if not models:
|
||||||
|
raise AttributeError('Please specify at least one voice model')
|
||||||
|
|
||||||
|
self.models = {}
|
||||||
|
for name, conf in models.items():
|
||||||
|
if name in self.models:
|
||||||
|
raise AttributeError('Duplicate model key {}'.format(name))
|
||||||
|
|
||||||
|
model_file = conf.get('voice_model_file')
|
||||||
|
if not model_file:
|
||||||
|
raise AttributeError('No voice_model_file specified for model {}'.format(name))
|
||||||
|
|
||||||
|
model_file = os.path.abspath(os.path.expanduser(model_file))
|
||||||
|
assistant_plugin_name = conf.get('assistant_plugin')
|
||||||
|
|
||||||
|
if not os.path.isfile(model_file):
|
||||||
|
raise FileNotFoundError('Voice model file {} does not exist or it not a regular file'.format(model_file))
|
||||||
|
|
||||||
|
self.models[name] = {
|
||||||
|
'voice_model_file': model_file,
|
||||||
|
'sensitivity': conf.get('sensitivity', 0.5),
|
||||||
|
'detect_sound': conf.get('detect_sound'),
|
||||||
|
'assistant_plugin': get_plugin(assistant_plugin_name) if assistant_plugin_name else None,
|
||||||
|
'assistant_language': conf.get('assistant_language'),
|
||||||
|
}
|
||||||
|
|
||||||
|
def hotword_detected(self, hotword):
|
||||||
"""
|
"""
|
||||||
Callback called on hotword detection
|
Callback called on hotword detection
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def callback():
|
def callback():
|
||||||
self.bus.post(HotwordDetectedEvent(hotword=self.hotword))
|
try:
|
||||||
|
import snowboydecoder
|
||||||
|
except ImportError:
|
||||||
|
import snowboy.snowboydecoder as snowboydecoder
|
||||||
|
|
||||||
if self.assistant_plugin:
|
self.bus.post(HotwordDetectedEvent(hotword=hotword))
|
||||||
# Trigger assistant conversation
|
model = self.models[hotword]
|
||||||
get_plugin(self.assistant_plugin).start_conversation()
|
|
||||||
|
detect_sound = model.get('detect_sound')
|
||||||
|
assistant_plugin = model.get('assistant_plugin')
|
||||||
|
assistant_language = model.get('assistant_language')
|
||||||
|
|
||||||
|
if detect_sound:
|
||||||
|
snowboydecoder.play_audio_file(detect_sound)
|
||||||
|
|
||||||
|
if assistant_plugin:
|
||||||
|
assistant_plugin.start_conversation(language=assistant_language)
|
||||||
|
|
||||||
return callback
|
return callback
|
||||||
|
|
||||||
|
@ -115,7 +154,10 @@ class AssistantSnowboyBackend(Backend):
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
super().run()
|
super().run()
|
||||||
self.detector.start(self.hotword_detected())
|
self.detector.start(detected_callback=[
|
||||||
|
lambda: self.hotword_detected(hotword)
|
||||||
|
for hotword in self.models.keys()
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
|
@ -8,7 +8,7 @@ class AssistantPlugin(ABC, Plugin):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def start_conversation(self, *args, **kwargs):
|
def start_conversation(self, *args, language=None, **kwargs):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
|
|
Loading…
Reference in a new issue