class PreciseConnection: """Represents a socket connection routed to a precise process""" def __init__(self, connection, address): self.address = address self.connection = connection # type: socket.socket self.stream = ReadWriteStream() self.runner = PreciseRunner( ListenerEngine(Listener(MODEL_NAME, CHUNK_SIZE), CHUNK_SIZE), 1, stream=self.stream, on_activation=self.on_activation, on_prediction=self.on_prediction ) self.runner.start() def on_activation(self): print(' --- ACTIVATION from:', self.address, '--- ') def on_prediction(self, conf): print('!' if conf > 0.5 else '.', end='', flush=True) def update(self): """Return whether connection still alive""" data = self.connection.recv(CHUNK_SIZE) self.stream.write(data) return bool(data) def close(self): print('Closing connection from', self.address) self.runner.stop() self.connection.close()
def __init__(self, key_phrase="hey mycroft", config=None, lang="en-us"): super(PreciseHotword, self).__init__(key_phrase, config, lang) from precise_runner import (PreciseRunner, PreciseEngine, ReadWriteStream) local_conf = LocalConf(USER_CONFIG) if local_conf.get('precise', {}).get('dist_url') == \ 'http://bootstrap.mycroft.ai/artifacts/static/daily/': del local_conf['precise']['dist_url'] local_conf.store() Configuration.updated(None) self.download_complete = True self.show_download_progress = Timer(0, lambda: None) precise_config = Configuration.get()['precise'] precise_exe = self.install_exe(precise_config['dist_url']) self.precise_model = self.install_model( precise_config['model_url'], key_phrase.replace(' ', '-')).replace('.tar.gz', '.pb') self.has_found = False self.stream = ReadWriteStream() def on_activation(): self.has_found = True self.runner = PreciseRunner(PreciseEngine(precise_exe, self.precise_model), stream=self.stream, on_activation=on_activation) self.runner.start()
class Precise(): def __init__(self): # engine data: https://github.com/mycroftai/precise-data/tree/dist # precise models: https://github.com/MycroftAI/precise-data/tree/models?files=1 sensitivity = float(0.5) trigger_level = int(3) model_path = "models/hey-mycroft.pb" engine_path = "precise-engine/precise-engine" engine = PreciseEngine( engine_path, model_path) self.stream = ReadWriteStream() self.runner = PreciseRunner( engine, sensitivity=sensitivity, trigger_level=trigger_level, on_activation=self.on_activation ) print("Starting precise") self.runner.start() while True: data = self.stream.read() self.update_runner(data) def update_runner(self, data): self.stream.write(data) def on_activation(self): print("Activation!")
def main(): args = create_parser(usage).parse_args() print('chunk_size: ', args.chunk_size) def on_activation(): activate_notify() if args.save_dir: global chunk_num nm = join(args.save_dir, args.save_prefix + session_id + '.' + str(chunk_num) + '.wav') save_audio(nm, audio_buffer) print() print('Saved to ' + nm + '.') chunk_num += 1 def on_prediction(conf): print('!' if conf > 0.8 else '.', end='', flush=True) listener = Listener(args.model, args.chunk_size) audio_buffer = np.zeros(listener.pr.buffer_samples, dtype=float) def get_prediction(chunk): nonlocal audio_buffer audio = buffer_to_audio(chunk) audio_buffer = np.concatenate((audio_buffer[len(audio):], audio)) return listener.update(chunk) engine = ListenerEngine(listener, args.chunk_size) engine.get_prediction = get_prediction runner = PreciseRunner(engine, args.threshold, on_activation=on_activation, on_prediction=on_prediction) runner.start() Event().wait() # Wait forever
def __init__(self, connection, address): self.address = address self.connection = connection # type: socket.socket self.stream = ReadWriteStream() self.runner = PreciseRunner( ListenerEngine(Listener(MODEL_NAME, CHUNK_SIZE), CHUNK_SIZE), 1, stream=self.stream, on_activation=self.on_activation, on_prediction=self.on_prediction ) self.runner.start()
def load_runner(self) -> None: """Load precise runner.""" if self.engine is None: # pylint: disable=E0401 from precise_runner import PreciseEngine self.model_name = self.profile.get("wake.precise.model", "hey-mycroft-2.pb") self.model_path = self.profile.read_path(self.model_name) self.engine_path = os.path.expandvars( self.profile.get("wake.precise.engine_path", "precise-engine")) self._logger.debug("Loading Precise engine at %s", self.engine_path) self.engine = PreciseEngine(self.engine_path, self.model_path, chunk_size=self.chunk_size) if self.runner is None: # pylint: disable=E0401 from precise_runner import PreciseRunner, ReadWriteStream self.stream = ReadWriteStream() sensitivity = float( self.profile.get("wake.precise.sensitivity", 0.5)) trigger_level = int( self.profile.get("wake.precise.trigger_level", 3)) def on_prediction(prob: float) -> None: self.prediction_sem.release() def on_activation() -> None: self.detected = True self.send(self.myAddress, "activated") self.runner = PreciseRunner( self.engine, stream=self.stream, sensitivity=sensitivity, trigger_level=trigger_level, on_activation=on_activation, on_prediction=on_prediction, ) assert self.runner is not None self.runner.start() self._logger.debug( "Loaded Mycroft Precise (model=%s, sensitivity=%s, trigger_level=%s)", self.model_path, sensitivity, trigger_level, )
def __init__(self, args): super().__init__(args) self.listener = Listener(args.model, args.chunk_size) self.audio_buffer = np.zeros(self.listener.pr.buffer_samples, dtype=float) self.engine = ListenerEngine(self.listener, args.chunk_size) self.engine.get_prediction = self.get_prediction self.runner = PreciseRunner(self.engine, args.trigger_level, sensitivity=args.sensitivity, on_activation=self.on_activation, on_prediction=self.on_prediction) self.session_id, self.chunk_num = '%09d' % randint(0, 999999999), 0
def __init__(self, args): super().__init__(args) if args.model == 'hey-mycroft': args.model = None self.engine = PreciseEngine(exe_file=None, model_file=args.model, chunk_size=args.chunk_size) self.runner = PreciseRunner(self.engine, args.trigger_level, sensitivity=args.sensitivity, on_activation=self.on_activation, on_prediction=self.on_prediction) self.session_id, self.chunk_num = '%09d' % randint(0, 999999999), 0
def run(self): def on_activation(): activate_notify() def on_prediction(conf): print('!' if conf > 0.5 else '.', end='', flush=True) args = self.args runner = PreciseRunner(ListenerEngine( PocketsphinxListener(args.key_phrase, args.dict_file, args.hmm_folder, args.threshold, args.chunk_size)), 3, on_activation=on_activation, on_prediction=on_prediction) runner.start() Event().wait() # Wait forever
def __init__(self): super().__init__() self._hotwordThread = None try: self._stream = ReadWriteStream() self._handler = PreciseRunner( PreciseEngine( exe_file=f'{self.Commons.rootDir()}/venv/bin/precise-engine', model_file=f'{self.Commons.rootDir()}/trained/hotwords/mycroft-precise/athena.pb' ), sensitivity=self.ConfigManager.getAliceConfigByName('wakewordSensitivity'), stream=self._stream, on_activation=self.hotwordSpotted ) except: self._enabled = False
def main(): args = create_parser(usage).parse_args() def on_activation(): Popen(['aplay', '-q', 'data/activate.wav']) def on_prediction(conf): print('!' if conf > 0.5 else '.', end='', flush=True) runner = PreciseRunner(ListenerEngine( PocketsphinxListener(args.key_phrase, args.dict_file, args.hmm_folder, args.threshold, args.chunk_size)), 3, on_activation=on_activation, on_prediction=on_prediction) runner.start() Event().wait() # Wait forever
def main(): args = create_parser(usage).parse_args() sensitivity = 0.5 def on_activation(): activate_notify() if args.save_dir: global chunk_num nm = join( args.save_dir, args.save_prefix + session_id + '.' + str(chunk_num) + '.wav') save_audio(nm, audio_buffer) print() print('Saved to ' + nm + '.') chunk_num += 1 def on_prediction(conf): if args.light_mode: print('!' if conf > 0.7 else '.', end='', flush=True) else: max_width = 80 width = min(get_terminal_size()[0], max_width) units = int(round(conf * width)) bar = 'X' * units + '-' * (width - units) cutoff = round((1.0 - sensitivity) * width) print(bar[:cutoff] + bar[cutoff:].replace('X', 'x')) listener = Listener(args.model, args.chunk_size) audio_buffer = np.zeros(listener.pr.buffer_samples, dtype=float) def get_prediction(chunk): nonlocal audio_buffer audio = buffer_to_audio(chunk) audio_buffer = np.concatenate((audio_buffer[len(audio):], audio)) return listener.update(chunk) engine = ListenerEngine(listener, args.chunk_size) engine.get_prediction = get_prediction runner = PreciseRunner(engine, args.threshold, sensitivity=sensitivity, on_activation=on_activation, on_prediction=on_prediction) runner.start() Event().wait() # Wait forever
class MycroftScript(BaseScript): usage = Usage(__doc__) def __init__(self, args): super().__init__(args) if args.model == 'hey-mycroft': args.model = None self.engine = PreciseEngine(exe_file=None, model_file=args.model, chunk_size=args.chunk_size) self.runner = PreciseRunner(self.engine, args.trigger_level, sensitivity=args.sensitivity, on_activation=self.on_activation, on_prediction=self.on_prediction) self.session_id, self.chunk_num = '%09d' % randint(0, 999999999), 0 def on_activation(self): activate_notify() if self.args.save_dir: nm = join( self.args.save_dir, self.args.save_prefix + self.session_id + '.' + str(self.chunk_num) + '.wav') save_audio(nm, self.audio_buffer) print() print('Saved to ' + nm + '.') self.chunk_num += 1 def on_prediction(self, conf): if self.args.basic_mode: print('!' if conf > 0.7 else '.', end='', flush=True) else: max_width = 80 width = min(get_terminal_size()[0], max_width) units = int(round(conf * width)) bar = 'X' * units + '-' * (width - units) cutoff = round((1.0 - self.args.sensitivity) * width) print(bar[:cutoff] + bar[cutoff:].replace('X', 'x')) def run(self): self.runner.start() Event().wait() # Wait forever
def __init__(self, key_phrase="hey mycroft", config=None, lang="en-us"): super().__init__(key_phrase, config, lang) global install_package from petact import install_package from precise_runner import (PreciseRunner, PreciseEngine, ReadWriteStream) local_conf = LocalConf(USER_CONFIG) if (local_conf.get('precise', {}).get('dist_url') == 'http://bootstrap.mycroft.ai/artifacts/static/daily/'): del local_conf['precise']['dist_url'] local_conf.store() Configuration.updated(None) self.download_complete = True self.show_download_progress = Timer(0, lambda: None) precise_config = Configuration.get()['precise'] precise_exe = self.update_precise(precise_config) local_model = self.config.get('local_model_file') if local_model: self.precise_model = expanduser(local_model) else: self.precise_model = self.install_model( precise_config['model_url'], key_phrase.replace(' ', '-')).replace('.tar.gz', '.pb') self.has_found = False self.stream = ReadWriteStream() def on_activation(): self.has_found = True trigger_level = self.config.get('trigger_level', 3) sensitivity = self.config.get('sensitivity', 0.5) self.runner = PreciseRunner( PreciseEngine(precise_exe, self.precise_model), trigger_level, sensitivity, stream=self.stream, on_activation=on_activation, ) self.runner.start()
def load_runner(self) -> None: if self.engine is None: from precise_runner import PreciseEngine self.model_name = self.profile.get("wake.precise.model") self.model_path = self.profile.read_path(self.model_name) self.engine_path = self.profile.get("wake.precise.engine_path") self._logger.debug(f"Loading Precise engine at {self.engine_path}") self.engine = PreciseEngine( self.engine_path, self.model_path, chunk_size=self.chunk_size ) if self.runner is None: from precise_runner import PreciseRunner self.stream = ByteStream() sensitivity = float(self.profile.get("wake.precise.sensitivity", 0.5)) trigger_level = int(self.profile.get("wake.precise.trigger_level", 3)) def on_prediction(prob: float) -> None: self.prediction_event.set() def on_activation() -> None: self.detected = True self.runner = PreciseRunner( self.engine, stream=self.stream, sensitivity=sensitivity, trigger_level=trigger_level, on_prediction=on_prediction, on_activation=on_activation, ) assert self.runner is not None self.runner.start() self._logger.debug( "Loaded Mycroft Precise (model=%s, sensitivity=%s, trigger_level=%s)" % (self.model_path, sensitivity, trigger_level) )
def main(): rospy.init_node('wake_word_detection_node') print("node is up") def on_activation(): print("activate") playsound(res_path + "/attention.wav") try: requests.get('http://www.google.com') try: response = stop_speech_perception_service(True) print(response) except rospy.ServiceException as exc: print("Service did not process request: " + str(exc)) except requests.ConnectionError: print("no internet") speak_pub.publish( "I'm sorry. I am not connected to the internet now and cannot answer" ) set_emotion_service(state="SADNESS", timeout=5500, restore=True) def on_prediction(conf): print(".") listener = Listener(res_path + "/stevie_10_06.pb", chunk_size) audio_buffer = np.zeros(listener.pr.buffer_samples, dtype=float) def get_prediction(chunk): nonlocal audio_buffer audio = buffer_to_audio(chunk) audio_buffer = np.concatenate((audio_buffer[len(audio):], audio)) return listener.update(chunk) engine = ListenerEngine(listener, chunk_size) engine.get_prediction = get_prediction runner = PreciseRunner(engine, trigger_level=3, sensitivity=0.5, on_activation=on_activation, on_prediction=on_prediction) runner.start() print("spinning") rospy.spin()
def main(): args = create_parser(usage).parse_args() def on_activation(): activate_notify() if args.save_dir: global chunk_num nm = join( args.save_dir, args.save_prefix + session_id + '.' + str(chunk_num) + '.wav') save_audio(nm, audio_buffer) print() print('Saved to ' + nm + '.') chunk_num += 1 def on_prediction(conf): global detecting_flag # print('!' if conf > 0.5 else '.', end='', flush=True) if conf > 0.5: detecting_flag = True if conf < 0.5 and detecting_flag: print(colored("Yeah! I'm Here.", 'green')) detecting_flag = False sunshine_model = './ok-sunshine.net' listener = Listener(sunshine_model, args.chunk_size) audio_buffer = np.zeros(listener.pr.buffer_samples, dtype=float) def get_prediction(chunk): nonlocal audio_buffer audio = buffer_to_audio(chunk) audio_buffer = np.concatenate((audio_buffer[len(audio):], audio)) return listener.update(chunk) engine = ListenerEngine(listener, args.chunk_size) engine.get_prediction = get_prediction runner = PreciseRunner(engine, args.threshold, on_activation=on_activation, on_prediction=on_prediction) runner.start() Event().wait() # Wait forever
def async_listen(call): nonlocal runner, detected_event hass.states.async_set(OBJECT_DECODER, STATE_LISTENING, state_attrs) engine = PreciseEngine('precise-engine', model) runner = PreciseRunner(engine, sensitivity=sensitivity, trigger_level=trigger_level, on_activation=lambda: detected_event.set()) # Runs in a separate thread detected_event.clear() runner.start() yield from asyncio.get_event_loop().run_in_executor(None, detected_event.wait) if not terminated: runner.stop() runner = None hass.states.async_set(OBJECT_DECODER, STATE_IDLE, state_attrs) # Fire detected event hass.bus.async_fire(EVENT_HOTWORD_DETECTED, { 'name': name, # name of the component 'model': model # model used })
def detect(): event = threading.Event() runner = PreciseRunner(engine, on_activation=lambda: event.set()) runner.start() event.wait() wake_statuses[system] = 'detected' runner.stop()
def __init__(self, factory, client, queue, sample_rate=16000): super(WebsocketAudioListener, self).__init__() self.client = client self.factory = factory self.sample_rate = sample_rate self.vad = webrtcvad.Vad(1) self.queue = queue self.hotword_found = False self.hotword_stream = ReadWriteStream() def on_activation(): self.hotword_found = True trigger_level = 1 sensitivity = 0.5 self.hotword_runner = PreciseRunner( PreciseEngine('/opt/backend/precise-engine/precise-engine', '/opt/backend/hey-mycroft.pb'), trigger_level, sensitivity, stream=self.hotword_stream, on_activation=on_activation, ) self.hotword_runner.start() BLOCKS_PER_SECOND = 50 self.block_size = int(self.sample_rate / float(BLOCKS_PER_SECOND)) # 320 padding_ms = 600 block_duration_ms = 1000 * \ self.block_size // self.sample_rate # 20 num_padding_blocks = padding_ms // block_duration_ms # 30 self.ratio = 0.75 self.ring_buffer = deque(maxlen=num_padding_blocks) self.triggered = False self.running = True
def __init__(self, key_phrase="hey mycroft", config=None, lang="en-us"): super().__init__(key_phrase, config, lang) from petact import install_package from precise_runner import ( PreciseRunner, PreciseEngine, ReadWriteStream ) global install_package self.download_complete = True self.show_download_progress = Timer(0, lambda: None) precise_config = CONFIGURATION['precise'] precise_exe = self.update_precise(precise_config) local_model = self.config.get('local_model_file') if local_model: self.precise_model = expanduser(local_model) else: self.precise_model = self.install_model( precise_config['model_url'], key_phrase.replace(' ', '-') ).replace('.tar.gz', '.pb') self.has_found = False self.stream = ReadWriteStream() def on_activation(): self.has_found = True trigger_level = self.config.get('trigger_level', 3) sensitivity = self.config.get('sensitivity', 0.5) self.runner = PreciseRunner( PreciseEngine(precise_exe, self.precise_model), trigger_level, sensitivity, stream=self.stream, on_activation=on_activation, ) self.runner.start()
def __init__(self, rt, on_activation: Callable): super().__init__(rt, on_activation) exe_file = which('precise-engine') precise_folder = join(self.rt.paths.user_config, 'precise') if not exe_file: exe_file = join(precise_folder, 'precise-engine', 'precise-engine') download_extract_tar( self.program_url.format(arch=platform.machine()), precise_folder, check_md5=False, subdir='precise-engine', on_update=lambda: self.rt.interfaces.faceplate.text('Updating listener...'), on_complete=lambda: self.rt.interfaces.faceplate.reset() ) log.debug('Using precise executable: ' + exe_file) model_folder = join(precise_folder, 'models', self.wake_word) model_file = join(model_folder, self.wake_word + '.pb') model_url = self.model_url.format(model_name=self.wake_word) download_extract_tar(model_url, model_folder, check_md5=True) from precise_runner import PreciseRunner, PreciseEngine engine = PreciseEngine(exe_file, model_file, chunk_size=1024) self.runner = PreciseRunner(engine, on_activation=on_activation)
def __init__(self, keyword=None, sensitivity=None, detected_callback=None ): super(HotwordDetector, self).__init__() sl = SettingLoader() self.settings = sl.settings self.paused_loop = False self.detected_callback = detected_callback self.sensitivity = sensitivity trigger_level = 3 self.keyword = keyword self.found_keyword = False if not os.path.exists(RESOURCE_FILE): if self.downloadPreciseEngine(): Utils.print_info("[Precise] Download complete") else: raise PreciseEngineNotFound("Error downloading precise engine, check your internet connection or try again later.") engine = PreciseEngine(RESOURCE_FILE, self.keyword) self.stream = ReadWriteStream() self.runner = PreciseRunner(engine, sensitivity=float(self.sensitivity), trigger_level=trigger_level, on_activation=self.activation ) self.runner.start() self.pause() # To avoid that precise starts detecting without beeing ready, we pause it right after start if self.settings.machine.startswith("arm"): # Because importing tensorflow takes up to 10 seconds, we sleep a while Utils.print_info("Starting precise trigger") time.sleep(10)
def main(): args = create_parser(usage).parse_args() def on_activation(): activate_notify() # TODO: trigger VMSE def on_prediction(conf): if args.basic_mode: print('!' if conf > 0.7 else '.', end='', flush=True) else: max_width = 80 width = min(get_terminal_size()[0], max_width) units = int(round(conf * width)) bar = 'X' * units + '-' * (width - units) cutoff = round((1.0 - args.sensitivity) * width) print(bar[:cutoff] + bar[cutoff:].replace('X', 'x')) listener = Listener(args.model, args.chunk_size) audio_buffer = np.zeros(listener.pr.buffer_samples, dtype=float) def get_prediction(chunk): nonlocal audio_buffer audio = buffer_to_audio(chunk) audio_buffer = np.concatenate((audio_buffer[len(audio):], audio)) return listener.update(chunk) engine = ListenerEngine(listener, args.chunk_size) engine.get_prediction = get_prediction runner = PreciseRunner(engine, args.trigger_level, sensitivity=args.sensitivity, on_activation=on_activation, on_prediction=on_prediction) runner.start() Event().wait() # Wait forever
class PreciseEngine(WakeWordEnginePlugin): program_url = ( 'https://raw.githubusercontent.com/MycroftAI/' 'precise-data/dist/{arch}/precise-engine.tar.gz' ) model_url = ( 'https://raw.githubusercontent.com/MycroftAI/' 'precise-data/models/{model_name}.tar.gz' ) def __init__(self, rt, on_activation: Callable): super().__init__(rt, on_activation) exe_file = which('precise-engine') precise_folder = join(self.rt.paths.user_config, 'precise') if not exe_file: exe_file = join(precise_folder, 'precise-engine', 'precise-engine') download_extract_tar( self.program_url.format(arch=platform.machine()), precise_folder, check_md5=False, subdir='precise-engine', on_update=lambda: self.rt.interfaces.faceplate.text('Updating listener...'), on_complete=lambda: self.rt.interfaces.faceplate.reset() ) log.debug('Using precise executable: ' + exe_file) model_folder = join(precise_folder, 'models', self.wake_word) model_file = join(model_folder, self.wake_word + '.pb') model_url = self.model_url.format(model_name=self.wake_word) download_extract_tar(model_url, model_folder, check_md5=True) from precise_runner import PreciseRunner, PreciseEngine engine = PreciseEngine(exe_file, model_file, chunk_size=1024) self.runner = PreciseRunner(engine, on_activation=on_activation) def startup(self): self.runner.start() def shutdown(self): self.runner.stop() def continue_listening(self): self.runner.play() def pause_listening(self): self.runner.pause()
def main(): parser = ArgumentParser('Implementation demo of precise-engine') parser.add_argument('engine', help='Location of binary engine file') parser.add_argument('model') args = parser.parse_args() def on_prediction(prob): print('!' if prob > 0.5 else '.', end='', flush=True) def on_activation(): Popen(['aplay', '-q', 'data/activate.wav']) engine = PreciseEngine(args.engine, args.model) PreciseRunner(engine, on_prediction=on_prediction, on_activation=on_activation, trigger_level=0).start() Event().wait() # Wait forever
def listen_for_wake_words(self, word_library, activation_notifier, startdelay): def on_prediction(prob): pass def on_activation(): if self.speech_locked: print("no activation, speech is locked") return print("ACTIVATION: {}".format(activation_notifier)) msg = RosString() msg.data = activation_notifier self.command_triggered_pub.publish(msg) if activation_notifier != "heyrobot" and activation_notifier != "stop_listening": if self.engines[activation_notifier][2]: self.start_stream_callback(activation_notifier) elif activation_notifier == "heyrobot": if self.engines[activation_notifier][2]: self.start_countdown() self.start_perpetual_stream_callback() else: print("auto 4") self.end_countdown() self.end_perpetual_stream() path = sys.argv[1] time.sleep(startdelay) print("{} ready".format(activation_notifier)) engine = PreciseEngine('{}/.venv/bin/precise-engine'.format(path), word_library) self.engines[activation_notifier][0] = PreciseRunner( engine, on_prediction=on_prediction, on_activation=on_activation, trigger_level=0) self.engines[activation_notifier][0].start()
def __init__(self, model): print("*** MODEL ***", model) self.wake_word = rospy.Publisher(WAKE_WORD_TOPIC, Bool, queue_size=10) self.stream = ReadWriteStream() rospy.Subscriber(CONVERTED_AUDIO_TOPIC, Float32MultiArray, self.audio_cb) engine = PreciseEngine(os.path.join(PRECISE_DIR, '.venv/bin/precise-engine'), model, chunk_size=1024) # stream = binary 16 bit mono! PreciseRunner(engine, stream=self.stream, on_prediction=self.on_prediction, on_activation=self.on_activation, trigger_level=3, sensitivity=0.8).start()
def __init__(self, key_phrase="hey mycroft", config=None, lang="en-us"): super(PreciseHotword, self).__init__(key_phrase, config, lang) from precise_runner import ( PreciseRunner, PreciseEngine, ReadWriteStream ) local_conf = LocalConf(USER_CONFIG) if local_conf.get('precise', {}).get('dist_url') == \ 'http://bootstrap.mycroft.ai/artifacts/static/daily/': del local_conf['precise']['dist_url'] local_conf.store() Configuration.updated(None) self.download_complete = True self.show_download_progress = Timer(0, lambda: None) precise_config = Configuration.get()['precise'] precise_exe = self.install_exe(precise_config['dist_url']) local_model = self.config.get('local_model_file') if local_model: self.precise_model = expanduser(local_model) else: self.precise_model = self.install_model( precise_config['model_url'], key_phrase.replace(' ', '-') ).replace('.tar.gz', '.pb') self.has_found = False self.stream = ReadWriteStream() def on_activation(): self.has_found = True self.runner = PreciseRunner( PreciseEngine(precise_exe, self.precise_model), stream=self.stream, on_activation=on_activation ) self.runner.start()
class PreciseWakeListener(RhasspyActor): """Listens for a wake word using Mycroft Precise.""" def __init__(self) -> None: # pylint: disable=E0401 from precise_runner import ReadWriteStream RhasspyActor.__init__(self) self.audio_buffer: bytes = bytes() self.audio_info: Dict[Any, Any] = {} self.chunk_delay = 0 self.chunk_size = 2048 self.detected: bool = False self.engine = None self.engine_path = "" self.model_name = "" self.model_path = "" self.prediction_sem = threading.Semaphore() self.preload = False self.receivers: List[RhasspyActor] = [] self.recorder: Optional[RhasspyActor] = None self.runner = None self.send_not_detected = False self.stream: Optional[ReadWriteStream] = None def to_started(self, from_state: str) -> None: """Transition to started state.""" self.recorder = self.config["recorder"] self.preload = self.config.get("preload", False) self.send_not_detected = self.config.get("not_detected", False) self.chunk_size = self.profile.get("wake.precise.chunk_size", 2048) self.chunk_delay = self.profile.get("wake.precise.chunk_delay", 0) if self.preload: try: self.load_runner() except Exception: pass self.transition("loaded") def in_loaded(self, message: Any, sender: RhasspyActor) -> None: """Handle messages in loaded state.""" if isinstance(message, ListenForWakeWord): try: self.load_runner() self.receivers.append(message.receiver or sender) self.transition("listening") if message.record: self.send(self.recorder, StartStreaming(self.myAddress)) except Exception: self._logger.exception("in_loaded") def in_listening(self, message: Any, sender: RhasspyActor) -> None: """Handle messages in listening state.""" try: if isinstance(message, AudioData): self.audio_info = message.info self.detected = False self.audio_buffer += message.data num_chunks = len(self.audio_buffer) // self.chunk_size if num_chunks > 0: assert self.stream is not None self.prediction_sem = threading.Semaphore() for _ in range(num_chunks): chunk = self.audio_buffer[:self.chunk_size] self.stream.write(chunk) self.audio_buffer = self.audio_buffer[self.chunk_size:] if self.send_not_detected: # Wait for all chunks to finish processing for _ in range(num_chunks): self.prediction_sem.acquire(timeout=0.1) # Wait a little bit for the precise engine to finish processing time.sleep(self.chunk_delay) if not self.detected: # Not detected not_detected_event = WakeWordNotDetected( self.model_name, audio_data_info=message.info) for receiver in self.receivers: self.send(receiver, not_detected_event) elif isinstance(message, StopListeningForWakeWord): self.receivers.remove(message.receiver or sender) if len(self.receivers) == 0: if message.record: self.send(self.recorder, StopStreaming(self.myAddress)) self.transition("loaded") elif isinstance(message, str): # Detected self._logger.debug("Hotword detected (%s)", self.model_name) detected_event = WakeWordDetected( self.model_name, audio_data_info=self.audio_info) for receiver in self.receivers: self.send(receiver, detected_event) except Exception: self._logger.exception("in_listening") def to_stopped(self, from_state: str) -> None: """Transition to stopped state.""" self.stream = None if self.runner is not None: self.runner.stop() # ------------------------------------------------------------------------- def load_runner(self) -> None: """Load precise runner.""" if self.engine is None: # pylint: disable=E0401 from precise_runner import PreciseEngine self.model_name = self.profile.get("wake.precise.model", "hey-mycroft-2.pb") self.model_path = self.profile.read_path(self.model_name) self.engine_path = os.path.expandvars( self.profile.get("wake.precise.engine_path", "precise-engine")) self._logger.debug("Loading Precise engine at %s", self.engine_path) self.engine = PreciseEngine(self.engine_path, self.model_path, chunk_size=self.chunk_size) if self.runner is None: # pylint: disable=E0401 from precise_runner import PreciseRunner, ReadWriteStream self.stream = ReadWriteStream() sensitivity = float( self.profile.get("wake.precise.sensitivity", 0.5)) trigger_level = int( self.profile.get("wake.precise.trigger_level", 3)) def on_prediction(prob: float) -> None: self.prediction_sem.release() def on_activation() -> None: self.detected = True self.send(self.myAddress, "activated") self.runner = PreciseRunner( self.engine, stream=self.stream, sensitivity=sensitivity, trigger_level=trigger_level, on_activation=on_activation, on_prediction=on_prediction, ) assert self.runner is not None self.runner.start() self._logger.debug( "Loaded Mycroft Precise (model=%s, sensitivity=%s, trigger_level=%s)", self.model_path, sensitivity, trigger_level, ) # ------------------------------------------------------------------------- def get_problems(self) -> Dict[str, Any]: """Get problems at startup.""" problems: Dict[str, Any] = {} try: # pylint: disable=E0401,W0611 from precise_runner import PreciseRunner, ReadWriteStream # noqa: F401 except Exception: problems[ "precise_runner not installed"] = "The precise_runner Python library is not installed. Try pip3 install precise_runner" engine_path = os.path.expandvars( self.profile.get("wake.precise.engine_path", "precise-engine")) if not os.path.exists(engine_path) and not shutil.which(engine_path): problems[ "Missing precise-engine"] = 'The Mycroft Precise engine is not installed. Follow the <a href="https://github.com/MycroftAI/mycroft-precise#binary-install">binary install instructions</a>.' model_name = self.profile.get("wake.precise.model", "hey-mycroft-2.pb") model_path = self.profile.read_path(model_name) if not os.path.exists(model_path): problems[ "Missing model"] = f"Your Mycroft Precise model could not be loaded from {model_path}" return problems
class PreciseHotword(HotWordEngine): """Precice is the default wakeword engine for mycroft. Precise is developed by Mycroft AI and produces quite good wake word spotting when trained on a decent dataset. """ def __init__(self, key_phrase="hey mycroft", config=None, lang="en-us"): super().__init__(key_phrase, config, lang) from precise_runner import ( PreciseRunner, PreciseEngine, ReadWriteStream ) local_conf = LocalConf(USER_CONFIG) if (local_conf.get('precise', {}).get('dist_url') == 'http://bootstrap.mycroft.ai/artifacts/static/daily/'): del local_conf['precise']['dist_url'] local_conf.store() Configuration.updated(None) self.download_complete = True self.show_download_progress = Timer(0, lambda: None) precise_config = Configuration.get()['precise'] precise_exe = self.update_precise(precise_config) local_model = self.config.get('local_model_file') if local_model: self.precise_model = expanduser(local_model) else: self.precise_model = self.install_model( precise_config['model_url'], key_phrase.replace(' ', '-') ).replace('.tar.gz', '.pb') self.has_found = False self.stream = ReadWriteStream() def on_activation(): self.has_found = True trigger_level = self.config.get('trigger_level', 3) sensitivity = self.config.get('sensitivity', 0.5) self.runner = PreciseRunner( PreciseEngine(precise_exe, self.precise_model), trigger_level, sensitivity, stream=self.stream, on_activation=on_activation, ) self.runner.start() def update_precise(self, precise_config): """Continously try to download precise until successful""" precise_exe = None while not precise_exe: try: precise_exe = self.install_exe(precise_config['dist_url']) except TriggerReload: raise except Exception as e: LOG.error( 'Precise could not be downloaded({})'.format(repr(e))) if exists(self.install_destination): precise_exe = self.install_destination else: # Wait one minute before retrying sleep(60) return precise_exe @property def folder(self): return join(expanduser('~'), '.mycroft', 'precise') @property def install_destination(self): return join(self.folder, 'precise-engine', 'precise-engine') def install_exe(self, url: str) -> str: url = url.format(arch=platform.machine()) if not url.endswith('.tar.gz'): url = requests.get(url).text.strip() if install_package( url, self.folder, on_download=self.on_download, on_complete=self.on_complete ): raise TriggerReload return self.install_destination def install_model(self, url: str, wake_word: str) -> str: model_url = url.format(wake_word=wake_word) model_file = join(self.folder, posixpath.basename(model_url)) try: install_package( model_url, self.folder, on_download=lambda: LOG.info('Updated precise model') ) except (HTTPError, ValueError): if isfile(model_file): LOG.info("Couldn't find remote model. Using local file") else: raise NoModelAvailable('Failed to download model:', model_url) return model_file @staticmethod def _snd_msg(cmd): with suppress(OSError): with open('/dev/ttyAMA0', 'w') as f: print(cmd, file=f) def on_download(self): LOG.info('Downloading Precise executable...') if isdir(join(self.folder, 'precise-stream')): rmtree(join(self.folder, 'precise-stream')) for old_package in glob(join(self.folder, 'precise-engine_*.tar.gz')): os.remove(old_package) self.download_complete = False self.show_download_progress = Timer( 5, self.during_download, args=[True] ) self.show_download_progress.start() def during_download(self, first_run=False): LOG.info('Still downloading executable...') if first_run: # TODO: Localize self._snd_msg('mouth.text=Updating listener...') if not self.download_complete: self.show_download_progress = Timer(30, self.during_download) self.show_download_progress.start() def on_complete(self): LOG.info('Precise download complete!') self.download_complete = True self.show_download_progress.cancel() self._snd_msg('mouth.reset') def update(self, chunk): self.stream.write(chunk) def found_wake_word(self, frame_data): if self.has_found: self.has_found = False return True return False def stop(self): if self.runner: self.runner.stop()
class PreciseHotword(HotWordEngine): def __init__(self, key_phrase="hey mycroft", config=None, lang="en-us"): super(PreciseHotword, self).__init__(key_phrase, config, lang) from precise_runner import ( PreciseRunner, PreciseEngine, ReadWriteStream ) local_conf = LocalConf(USER_CONFIG) if local_conf.get('precise', {}).get('dist_url') == \ 'http://bootstrap.mycroft.ai/artifacts/static/daily/': del local_conf['precise']['dist_url'] local_conf.store() Configuration.updated(None) self.download_complete = True self.show_download_progress = Timer(0, lambda: None) precise_config = Configuration.get()['precise'] precise_exe = self.install_exe(precise_config['dist_url']) local_model = self.config.get('local_model_file') if local_model: self.precise_model = expanduser(local_model) else: self.precise_model = self.install_model( precise_config['model_url'], key_phrase.replace(' ', '-') ).replace('.tar.gz', '.pb') self.has_found = False self.stream = ReadWriteStream() def on_activation(): self.has_found = True self.runner = PreciseRunner( PreciseEngine(precise_exe, self.precise_model), stream=self.stream, on_activation=on_activation ) self.runner.start() @property def folder(self): return join(expanduser('~'), '.mycroft', 'precise') def install_exe(self, url: str) -> str: url = url.format(arch=platform.machine()) if not url.endswith('.tar.gz'): url = requests.get(url).text.strip() if install_package( url, self.folder, on_download=self.on_download, on_complete=self.on_complete ): raise TriggerReload return join(self.folder, 'precise-engine', 'precise-engine') def install_model(self, url: str, wake_word: str) -> str: model_url = url.format(wake_word=wake_word) model_file = join(self.folder, posixpath.basename(model_url)) try: install_package( model_url, self.folder, on_download=lambda: LOG.info('Updated precise model') ) except (HTTPError, ValueError): if isfile(model_file): LOG.info("Couldn't find remote model. Using local file") else: raise NoModelAvailable('Failed to download model:', model_url) return model_file @staticmethod def _snd_msg(cmd): with suppress(OSError): with open('/dev/ttyAMA0', 'w') as f: print(cmd, file=f) def on_download(self): LOG.info('Downloading Precise executable...') if isdir(join(self.folder, 'precise-stream')): rmtree(join(self.folder, 'precise-stream')) for old_package in glob(join(self.folder, 'precise-engine_*.tar.gz')): os.remove(old_package) self.download_complete = False self.show_download_progress = Timer( 5, self.during_download, args=[True] ) self.show_download_progress.start() def during_download(self, first_run=False): LOG.info('Still downloading executable...') if first_run: # TODO: Localize self._snd_msg('mouth.text=Updating listener...') if not self.download_complete: self.show_download_progress = Timer(30, self.during_download) self.show_download_progress.start() def on_complete(self): LOG.info('Precise download complete!') self.download_complete = True self.show_download_progress.cancel() self._snd_msg('mouth.reset') def update(self, chunk): self.stream.write(chunk) def found_wake_word(self, frame_data): if self.has_found: self.has_found = False return True return False def stop(self): if self.runner: self.runner.stop()