示例#1
0
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()
示例#2
0
class PreciseWakeword(WakewordEngine):

    NAME = 'Precise'
    DEPENDENCIES = {'system': [], 'pip': {'mycroft-precise==0.3.0'}}

    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 onBooted(self):
        super().onBooted()
        if self._enabled:
            if not self._handler:
                self.logWarning('Hotword engine failed to init')
            else:
                self._handler.start()

    def onStop(self):
        super().onStop()
        if self._handler:
            self._handler.stop()

    def hotwordSpotted(self):
        self.logDebug('Detected wakeword')
        self._handler.pause()

        self.MqttManager.publish(
            topic=constants.TOPIC_HOTWORD_DETECTED.format('default'),
            payload={
                'siteId':
                self.ConfigManager.getAliceConfigByName('uuid'),
                'modelId':
                f'precise_athena',
                'modelVersion':
                '0.3.0',
                'modelType':
                'universal',
                'currentSensitivity':
                self.ConfigManager.getAliceConfigByName('wakewordSensitivity')
            })

    def onHotwordToggleOn(self, siteId: str, session: DialogSession):
        if self._enabled and self._handler:
            self._handler.start()

    def onAudioFrame(self, message: MQTTMessage, siteId: str):
        if not self.enabled or not self._handler or self._handler.is_paused or self._stream is None:
            return

        with io.BytesIO(message.payload) as buffer:
            try:
                with wave.open(buffer, 'rb') as wav:
                    frame = wav.readframes(self.AudioServer.FRAMES_PER_BUFFER)
                    while frame:
                        self._stream.write(frame)
                        frame = wav.readframes(
                            self.AudioServer.FRAMES_PER_BUFFER)

            except Exception as e:
                self.logError(f'Error recording audio frame: {e}')
示例#3
0
class HotwordDetector(Thread):
    """
    Precise decoder to detect whether a keyword specified by `decoder_model`
    exists in a microphone input stream.

    :param decoder_model: decoder model file path, a string or a list of strings
    :param sensitivity: decoder sensitivity, a float of a list of floats.
                              The bigger the value, the more senstive the
                              decoder. If an empty list is provided, then the
                              default sensitivity in the model will be used.
    """
    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 run(self):
        logger.debug("detecting...")
        while True:
            if not self.paused_loop:
                data = self.stream.read()
                if len(data) > 0:
                    self.stream.write(data)

                if self.found_keyword:
                    self.pause()                          # We start pausing it here, to avoid double activations
                    message = "[Precise] Keyword detected"
                    Utils.print_info(message)
                    logger.debug(message)
                    self.detected_callback()

            time.sleep(0.01)
        logger.debug("finished")


    def activation(self):
        self.found_keyword = True

    def pause(self):
        self.runner.pause()
        self.paused_loop = True

    def unpause(self):
        self.runner.play()
        self.paused_loop = False
        self.found_keyword = False
    
    def downloadPreciseEngine(self):
        import json
        import requests
        import tarfile

        Utils.print_info("[Preicse] Precise engine not present, starting download now")
        url = "https://api.github.com/repos/MycroftAI/mycroft-precise/releases/latest"
        response = requests.get(url)
        if response.status_code == 200:
            download_url = None
            arch = self.settings.machine
            for asset in response.json()["assets"]:
                if arch in asset.get("name"):
                    if asset.get("name").startswith("precise-engine") and asset.get("name").endswith(".tar.gz"):
                        download_name = asset.get("name")
                        download_url = asset.get('browser_download_url')

            filepath = os.path.join(TOP_DIR, download_name)
            if download_url:
                Utils.print_info("[Precise] Downloading %s this can take a moment" % download_name)
                file = requests.get(download_url)
                if file.status_code == 200:
                    with open(filepath, 'wb') as f:
                        f.write(file.content)

                    with tarfile.open(filepath) as tar:
                        tar.extractall(path=TOP_DIR)
                    os.remove(filepath)
                    return True
        return False