Browse Source

add precompiled snowboy lib

Nicolas Marcq 8 years ago
parent
commit
300ab57bf0

+ 0 - 0
lib/Neurone.py → core/Neurone.py


+ 0 - 0
lib/__init__.py → core/__init__.py


+ 1 - 1
plugins/system_date/system_date.py

@@ -1,6 +1,6 @@
 #!/usr/bin/python
 import time
-from lib import Neurone
+from core import Neurone
 
 
 class SystemDate(Neurone):

+ 0 - 0
stt/__init__.py


+ 0 - 0
stt/snowboy/__init__.py


BIN
stt/snowboy/resources/common.res


BIN
stt/snowboy/resources/ding.wav


BIN
stt/snowboy/resources/dong.wav


BIN
stt/snowboy/resources/snowboy.umdl


+ 185 - 0
stt/snowboy/snowboydecoder.py

@@ -0,0 +1,185 @@
+#!/usr/bin/env python
+
+import collections
+import pyaudio
+import snowboydetect
+import time
+import wave
+import os
+import logging
+
+logging.basicConfig()
+logger = logging.getLogger("snowboy")
+logger.setLevel(logging.INFO)
+TOP_DIR = os.path.dirname(os.path.abspath(__file__))
+
+RESOURCE_FILE = os.path.join(TOP_DIR, "resources/common.res")
+DETECT_DING = os.path.join(TOP_DIR, "resources/ding.wav")
+DETECT_DONG = os.path.join(TOP_DIR, "resources/dong.wav")
+
+
+class RingBuffer(object):
+    """Ring buffer to hold audio from PortAudio"""
+    def __init__(self, size = 4096):
+        self._buf = collections.deque(maxlen=size)
+
+    def extend(self, data):
+        """Adds data to the end of buffer"""
+        self._buf.extend(data)
+
+    def get(self):
+        """Retrieves data from the beginning of buffer and clears it"""
+        tmp = ''.join(self._buf)
+        self._buf.clear()
+        return tmp
+
+
+def play_audio_file(fname=DETECT_DING):
+    """Simple callback function to play a wave file. By default it plays
+    a Ding sound.
+
+    :param str fname: wave file name
+    :return: None
+    """
+    ding_wav = wave.open(fname, 'rb')
+    ding_data = ding_wav.readframes(ding_wav.getnframes())
+    audio = pyaudio.PyAudio()
+    stream_out = audio.open(
+        format=audio.get_format_from_width(ding_wav.getsampwidth()),
+        channels=ding_wav.getnchannels(),
+        rate=ding_wav.getframerate(), input=False, output=True)
+    stream_out.start_stream()
+    stream_out.write(ding_data)
+    time.sleep(0.2)
+    stream_out.stop_stream()
+    stream_out.close()
+    audio.terminate()
+
+
+class HotwordDetector(object):
+    """
+    Snowboy 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 resource: resource file path.
+    :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.
+    :param audio_gain: multiply input volume by this factor.
+    """
+    def __init__(self, decoder_model,
+                 resource=RESOURCE_FILE,
+                 sensitivity=[],
+                 audio_gain=1):
+
+        def audio_callback(in_data, frame_count, time_info, status):
+            self.ring_buffer.extend(in_data)
+            play_data = chr(0) * len(in_data)
+            return play_data, pyaudio.paContinue
+
+        tm = type(decoder_model)
+        ts = type(sensitivity)
+        if tm is not list:
+            decoder_model = [decoder_model]
+        if ts is not list:
+            sensitivity = [sensitivity]
+        model_str = ",".join(decoder_model)
+
+        self.detector = snowboydetect.SnowboyDetect(
+            resource_filename=resource, model_str=model_str)
+        self.detector.SetAudioGain(audio_gain)
+        self.num_hotwords = self.detector.NumHotwords()
+
+        if len(decoder_model) > 1 and len(sensitivity) == 1:
+            sensitivity = sensitivity*self.num_hotwords
+        if len(sensitivity) != 0:
+            assert self.num_hotwords == len(sensitivity), \
+                "number of hotwords in decoder_model (%d) and sensitivity " \
+                "(%d) does not match" % (self.num_hotwords, len(sensitivity))
+        sensitivity_str = ",".join([str(t) for t in sensitivity])
+        if len(sensitivity) != 0:
+            self.detector.SetSensitivity(sensitivity_str);
+
+        self.ring_buffer = RingBuffer(
+            self.detector.NumChannels() * self.detector.SampleRate() * 5)
+        self.audio = pyaudio.PyAudio()
+        self.stream_in = self.audio.open(
+            input=True, output=False,
+            format=self.audio.get_format_from_width(
+                self.detector.BitsPerSample() / 8),
+            channels=self.detector.NumChannels(),
+            rate=self.detector.SampleRate(),
+            frames_per_buffer=2048,
+            stream_callback=audio_callback)
+
+
+    def start(self, detected_callback=play_audio_file,
+              interrupt_check=lambda: False,
+              sleep_time=0.03):
+        """
+        Start the voice detector. For every `sleep_time` second it checks the
+        audio buffer for triggering keywords. If detected, then call
+        corresponding function in `detected_callback`, which can be a single
+        function (single model) or a list of callback functions (multiple
+        models). Every loop it also calls `interrupt_check` -- if it returns
+        True, then breaks from the loop and return.
+
+        :param detected_callback: a function or list of functions. The number of
+                                  items must match the number of models in
+                                  `decoder_model`.
+        :param interrupt_check: a function that returns True if the main loop
+                                needs to stop.
+        :param float sleep_time: how much time in second every loop waits.
+        :return: None
+        """
+        if interrupt_check():
+            logger.debug("detect voice return")
+            return
+
+        tc = type(detected_callback)
+        if tc is not list:
+            detected_callback = [detected_callback]
+        if len(detected_callback) == 1 and self.num_hotwords > 1:
+            detected_callback *= self.num_hotwords
+
+        assert self.num_hotwords == len(detected_callback), \
+            "Error: hotwords in your models (%d) do not match the number of " \
+            "callbacks (%d)" % (self.num_hotwords, len(detected_callback))
+
+        logger.debug("detecting...")
+
+        while True:
+            if interrupt_check():
+                logger.debug("detect voice break")
+                break
+            data = self.ring_buffer.get()
+            if len(data) == 0:
+                time.sleep(sleep_time)
+                continue
+
+            ans = self.detector.RunDetection(data)
+            if ans == -1:
+                logger.warning("Error initializing streams or reading audio data")
+            elif ans == -2:
+                logger.debug("Silence")
+            elif ans > 0:
+                message = "Keyword " + str(ans) + " detected at time: "
+                message += time.strftime("%Y-%m-%d %H:%M:%S",
+                                         time.localtime(time.time()))
+                logger.info(message)
+                callback = detected_callback[ans-1]
+                if callback is not None:
+                    callback()
+
+        logger.debug("finished.")
+
+    def terminate(self):
+        """
+        Terminate audio stream. Users cannot call start() again to detect.
+        :return: None
+        """
+        self.stream_in.stop_stream()
+        self.stream_in.close()
+        self.audio.terminate()

+ 143 - 0
stt/snowboy/snowboydetect.py

@@ -0,0 +1,143 @@
+# This file was automatically generated by SWIG (http://www.swig.org).
+# Version 3.0.7
+#
+# Do not make changes to this file unless you know what you are doing--modify
+# the SWIG interface file instead.
+
+
+
+
+
+from sys import version_info
+if version_info >= (2, 6, 0):
+    def swig_import_helper():
+        from os.path import dirname
+        import imp
+        fp = None
+        try:
+            fp, pathname, description = imp.find_module('_snowboydetect', [dirname(__file__)])
+        except ImportError:
+            import _snowboydetect
+            return _snowboydetect
+        if fp is not None:
+            try:
+                _mod = imp.load_module('_snowboydetect', fp, pathname, description)
+            finally:
+                fp.close()
+            return _mod
+    _snowboydetect = swig_import_helper()
+    del swig_import_helper
+else:
+    import _snowboydetect
+del version_info
+try:
+    _swig_property = property
+except NameError:
+    pass  # Python < 2.2 doesn't have 'property'.
+
+
+def _swig_setattr_nondynamic(self, class_type, name, value, static=1):
+    if (name == "thisown"):
+        return self.this.own(value)
+    if (name == "this"):
+        if type(value).__name__ == 'SwigPyObject':
+            self.__dict__[name] = value
+            return
+    method = class_type.__swig_setmethods__.get(name, None)
+    if method:
+        return method(self, value)
+    if (not static):
+        if _newclass:
+            object.__setattr__(self, name, value)
+        else:
+            self.__dict__[name] = value
+    else:
+        raise AttributeError("You cannot add attributes to %s" % self)
+
+
+def _swig_setattr(self, class_type, name, value):
+    return _swig_setattr_nondynamic(self, class_type, name, value, 0)
+
+
+def _swig_getattr_nondynamic(self, class_type, name, static=1):
+    if (name == "thisown"):
+        return self.this.own()
+    method = class_type.__swig_getmethods__.get(name, None)
+    if method:
+        return method(self)
+    if (not static):
+        return object.__getattr__(self, name)
+    else:
+        raise AttributeError(name)
+
+def _swig_getattr(self, class_type, name):
+    return _swig_getattr_nondynamic(self, class_type, name, 0)
+
+
+def _swig_repr(self):
+    try:
+        strthis = "proxy of " + self.this.__repr__()
+    except:
+        strthis = ""
+    return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,)
+
+try:
+    _object = object
+    _newclass = 1
+except AttributeError:
+    class _object:
+        pass
+    _newclass = 0
+
+
+class SnowboyDetect(_object):
+    __swig_setmethods__ = {}
+    __setattr__ = lambda self, name, value: _swig_setattr(self, SnowboyDetect, name, value)
+    __swig_getmethods__ = {}
+    __getattr__ = lambda self, name: _swig_getattr(self, SnowboyDetect, name)
+    __repr__ = _swig_repr
+
+    def __init__(self, resource_filename, model_str):
+        this = _snowboydetect.new_SnowboyDetect(resource_filename, model_str)
+        try:
+            self.this.append(this)
+        except:
+            self.this = this
+
+    def Reset(self):
+        return _snowboydetect.SnowboyDetect_Reset(self)
+
+    def RunDetection(self, *args):
+        return _snowboydetect.SnowboyDetect_RunDetection(self, *args)
+
+    def SetSensitivity(self, sensitivity_str):
+        return _snowboydetect.SnowboyDetect_SetSensitivity(self, sensitivity_str)
+
+    def GetSensitivity(self):
+        return _snowboydetect.SnowboyDetect_GetSensitivity(self)
+
+    def SetAudioGain(self, audio_gain):
+        return _snowboydetect.SnowboyDetect_SetAudioGain(self, audio_gain)
+
+    def UpdateModel(self):
+        return _snowboydetect.SnowboyDetect_UpdateModel(self)
+
+    def NumHotwords(self):
+        return _snowboydetect.SnowboyDetect_NumHotwords(self)
+
+    def SampleRate(self):
+        return _snowboydetect.SnowboyDetect_SampleRate(self)
+
+    def NumChannels(self):
+        return _snowboydetect.SnowboyDetect_NumChannels(self)
+
+    def BitsPerSample(self):
+        return _snowboydetect.SnowboyDetect_BitsPerSample(self)
+    __swig_destroy__ = _snowboydetect.delete_SnowboyDetect
+    __del__ = lambda self: None
+SnowboyDetect_swigregister = _snowboydetect.SnowboyDetect_swigregister
+SnowboyDetect_swigregister(SnowboyDetect)
+
+# This file is compatible with both classic and new-style classes.
+
+

+ 35 - 0
test.py

@@ -0,0 +1,35 @@
+from stt.snowboy import snowboydecoder
+import sys
+import signal
+
+interrupted = False
+
+
+def signal_handler(signal, frame):
+    global interrupted
+    interrupted = True
+
+
+def interrupt_callback():
+    global interrupted
+    return interrupted
+
+# if len(sys.argv) == 1:
+#     print("Error: need to specify model name")
+#     print("Usage: python demo.py your.model")
+#     sys.exit(-1)
+
+model = "stt/snowboy/resources/snowboy.umdl"
+
+# capture SIGINT signal, e.g., Ctrl+C
+signal.signal(signal.SIGINT, signal_handler)
+
+detector = snowboydecoder.HotwordDetector(model, sensitivity=0.5)
+print('Listening... Press Ctrl+C to exit')
+
+# main loop
+detector.start(detected_callback=snowboydecoder.play_audio_file,
+               interrupt_check=interrupt_callback,
+               sleep_time=0.03)
+
+detector.terminate()