Exemplo n.º 1
0
    def handle(self):
        '''
        @see Handler.handle()
        '''
        try:
            value = parse_number(self._volume)
            LOG.info("Got value of %s from %s" % (value, self._volume))

            if value < 0 or value > 11:
                # Bad value
                return Result(
                    self, "Sorry, volume needs to be between zero and eleven",
                    False, True)
            else:
                # Acknowledge that it happened
                set_volume(value)
                return Result(self, "Okay, volume now %s" % (value, ), False,
                              True)

        except Exception:
            LOG.error("Problem parsing volume '%s':\n%s" %
                      (self._volume, traceback.format_exc()))
            return Result(
                self, "Sorry, I don't know how to set the volume to %s" %
                (self._volume, ), False, True)
Exemplo n.º 2
0
    def evaluate(self, tokens):
        """
        @see Service.evaluate()
        """
        # Get the words, ready to match with
        words = self._words(tokens)

        # Look for these prefixes
        prefices = (('spell', ), ('how', 'do', 'you', 'spell'))
        match = None
        for prefix in prefices:
            try:
                # Look for the prefix and suffix in the words
                (start, end, score) = fuzzy_list_range(words, prefix)
                LOG.debug("%s matches %s with from %d to %d with score %d",
                          prefix, words, start, end, score)

                # Get the best one
                if (start == 0 and (match is None or match[2] < score)):
                    match = (start, end, score)
            except ValueError:
                pass

        # Did we get anything?
        if match is not None:
            # Simply give these to the handler
            (start, end, score) = match
            return _SpellingHandler(self, tokens, score / 100.0, words[end:])
        else:
            # Nope, we got nothing
            return None
Exemplo n.º 3
0
 def acceptor():
     while self._running:
         (sckt, addr) = self._socket.accept()
         LOG.info("Got connection from %s" % (addr, ))
         thread = Thread(target=lambda: self._handle(sckt))
         thread.daemon = True
         thread.start()
Exemplo n.º 4
0
 def handle(self):
     '''
     @see Handler.handle()`
     '''
     LOG.info('Playing %s' % (self._what))
     self.service.play(self._filenames)
     return Result(self, '', False, True)
Exemplo n.º 5
0
    def evaluate(self, tokens):
        """
        @see Service.evaluate()
        """
        # Get the words, ready to match with
        words = self._words(tokens)

        # This is how it could be phrased
        fixes = ((('define', ), tuple()), (('what', 'is', 'the', 'meaning',
                                            'of'), tuple()), (('what', 'does'),
                                                              ('mean', )))
        match = None
        for (prefix, suffix) in fixes:
            try:
                # Look for the prefix and suffix in the words
                if len(prefix) > 0:
                    (pre_start, pre_end,
                     pre_score) = fuzzy_list_range(words, prefix)
                else:
                    (pre_start, pre_end, pre_score) = (0, 0, 100)
                if len(suffix) > 0:
                    (suf_start, suf_end,
                     suf_score) = fuzzy_list_range(words, suffix)
                else:
                    (suf_start, suf_end, suf_score) = (len(words), len(words),
                                                       100)
                LOG.debug(
                    "%s matches %s with from %d to %d with score %d, "
                    "and %s matches from %d to %d with score %d", prefix,
                    words, pre_start, pre_end, pre_score, suffix, suf_start,
                    suf_end, suf_score)

                # We expect there to be only one word in the middle of the
                # prefix and suffix when we match
                if (pre_start == 0 and pre_end + 1 == suf_start
                        and suf_end == len(words)
                        and (match is None or match[2] < score)):
                    match = (pre_start, pre_end, pre_score, suf_start, suf_end,
                             suf_score)
            except ValueError:
                pass

        # Did we get anything?
        if match is not None:
            # Pull back the values
            (pre_start, pre_end, pre_score, suf_start, suf_end,
             suf_score) = match

            # The belief is the geometric distance of the scores
            belief = sqrt(pre_score * pre_score +
                          suf_score * suf_score) / 100.0

            # The word is the one at pre_end (since it's non-inclusive)
            word = words[pre_end]

            # And give back the handler
            return _DictionaryHandler(self, tokens, belief, word, self._limit)
        else:
            # Nope, we got nothing
            return None
Exemplo n.º 6
0
    def play_files(self, filenames):
        """
        Play a list of files.

        :type  filenames: tuple(str)
        :param filenames:
            The list of filenames to play.
        """
        # First we stop everything
        self.stop()

        # Make sure that we have at least one file
        if len(filenames) == 0:
            return

        # Now we load the first file. We do this directly so that errors may
        # propagate.
        filename = filenames[0]
        LOG.info("Playing %s", filename)
        get_pygame().mixer.music.load(filename)
        get_pygame().mixer.music.play()

        # And enqueue the rest
        for filename in filenames[1:]:
            self._queue.put(filename)
Exemplo n.º 7
0
    def __init__(self, roots):
        """
        :type  roots: tuple(str)
        :param roots:
            The list of URLs to search for music on. In the simplest form these
            will likely just be a bunch of strings looking something like:
                C{file:///home/pi/Music}
        """
        # The various indices. These are of the form <str,tuple(_Entry)>.
        self._by_name = {}
        self._by_artist = {}
        self._by_album = {}

        # Tupleize, just in case someone passed in a string or whathaveyou
        if isinstance(roots, str):
            roots = (roots, )
        elif not isinstance(roots, (tuple, list)):
            roots = tuple(roots)

        if roots is not None:
            for root in roots:
                start = time.time()
                self._build(root)
                end = time.time()
                LOG.info("Indexed %s in %0.1f seconds", root, end - start)
Exemplo n.º 8
0
    def handle(self):
        '''
        @see Handler.handle()
        '''
        try:
            LOG.info("Querying Wikipedia for '%s'" % (self._thing, ))
            summary = wikipedia.summary(self._thing)
        except Exception as e:
            LOG.error("Failed to query Wikipedia about '%s': %s" %
                      (self._thing, e))
            return Result(
                self, "Sorry, there was a problem asking Wikipedia about %s" %
                (self._thing, ), False, True)

        # Anything?
        if summary is None or len(summary.strip()) == 0:
            return None

        # Strip the summary down a little, some of these can be pretty
        # long. First just grab the first paragraph. Next, stop after about
        # 400 chars.
        shortened = summary.split('\n')[0]
        if '. ' in shortened and len(shortened) > 400:
            index = shortened.index('. ')
            shortened = shortened[:index + 1]

        # And give it back. We use a period after "says" here so that the speech
        # output will pause appropriately. It's not good gramma though. Since we
        # got back a result then we mark ourselves as exclusive; there is
        # probably not a lot of point in having others also return information.
        return Result(self, "Wikipedia says.\n%s" % shortened, False, True)
Exemplo n.º 9
0
    def handle(self):
        """
        @see Handler.handle()
        """
        try:
            # Make the change, capping at min and max
            cur = get_volume()
            new = max(MIN_VOLUME, min(MAX_VOLUME, cur + self._delta))

            # Any change?
            if cur != new:
                # Acknowledge that it happened
                set_volume(new)
                direction = "Up" if self._delta > 0 else "Down"
                return Result(self, direction, False, True)
            else:
                # Nothing to do
                return None

        except Exception:
            LOG.error("Problem setting changing the volume by %s:\n%s" %
                      (self._delta, traceback.format_exc()))
            return Result(self,
                          "Sorry, there was a problem changing the volume",
                          False, True)
Exemplo n.º 10
0
    def handle(self):
        """
        @see Handler.handle()
        """
        try:
            value = parse_number(self._volume)
            LOG.info("Got value of %s from %s" % (value, self._volume))

            if value < MIN_VOLUME or value > MAX_VOLUME:
                # Bad value
                return Result(
                    self, "Sorry, volume needs to be between %d and %d" %
                    (MIN_VOLUME, MAX_VOLUME), False, True)
            else:
                # Acknowledge that it happened
                set_volume(value)
                return Result(self, "Okay, volume now %s" % (value, ), False,
                              True)

        except Exception:
            LOG.error("Problem parsing volume '%s':\n%s" %
                      (self._volume, traceback.format_exc()))
            return Result(
                self, "Sorry, I don't know how to set the volume to %s" %
                (self._volume, ), False, True)
Exemplo n.º 11
0
 def handle(self):
     """
     @see Handler.handle()`
     """
     LOG.info('Playing %s' % (self._what))
     self.service.play(self._token)
     return Result(self, '', False, True)
Exemplo n.º 12
0
    def _decode(self):
        """
        @see AudioInput._decode()
        """
        # Collect anything remaining
        self._add_result(self._recognizer.FinalResult())

        # Ensure it's clear for next time
        self._recognizer.Reset()

        # Tokenize
        tokens = []
        LOG.debug("Decoding: %s" % self._results)
        for result in self._results:
            word = result.get('word', '').strip()
            conf = result.get('conf', 0.0)
            if word and conf:
                tokens.append(Token(word, conf, True))

        # Done
        self._results = []

        # And give them all back
        LOG.debug("Got: %s" % ' '.join(str(i) for i in tokens))
        return tokens
Exemplo n.º 13
0
    def _run(self):
        """
        The actual worker thread.
        """
        # Festival is a little hokey and so we need to start it in the same
        # thread that we use it. Otherwise we get an message saying:
        #  SIOD ERROR: the currently assigned stack limit has been exceeded
        # As such we need to do everything here and communicate back success or
        # failure to the _start() method via member variables. Lovely.
        try:
            import festival
            festival.execCommand(self._voice)
            self._boot_strapped = True
        except Exception as e:
            self._start_error = e
            return

        # Keep going until we're told to stop
        while self.is_running:
            if len(self._queue) == 0:
                time.sleep(0.1)
                continue

            # Else we have something to say
            try:
                # Get the text, make sure that '"'s in it won't confuse things
                start = time.time()
                text = self._queue.pop()
                text = text.replace('"', '')

                # Festvial pauses for too long with commas so just ignore them
                text = text.replace(',', '')

                # Ignore empty strings
                if not text:
                    LOG.info("Nothing to say...")
                    continue

                # We're about to say something, clear any interrupted flag ready
                # for any new one
                self._interrupted = False

                # We're talking so mark ourselves as active accordingly
                self._notify(Notifier.WORKING)

                # Break up the text into bits and say them so that we can
                # interrupt the output. Then say each non-empty part. We break
                # on natural pauses in the speech.
                for part in re.split(r'[\.,;:]', text):
                    if self._interrupted:
                        break
                    if part:
                        festival.sayText(part)

            except Exception as e:
                LOG.error("Failed to say '%s': %s" % (text, e))

            finally:
                self._notify(Notifier.IDLE)
Exemplo n.º 14
0
 def _start(self):
     """
     @see Startable._start()
     """
     # Create the socket
     LOG.info("Opening socket to %s:%d" % (self._host, self._port))
     self._socket = socket.socket()
     self._socket.connect((self._host, self._port))
Exemplo n.º 15
0
 def _on_button(self, button):
     """
     Handle a button press.
     """
     number = button.pin.number
     LOG.info("Got button on pin GPIO%d" % (number,))
     tokens = self._bindings.get(number, [])
     if len(tokens) > 0:
         self._output.append(tuple(self._prefix + tokens))
Exemplo n.º 16
0
 def _on_key(self, app, what):
     """
     Handle an incoming media key.
     """
     if app == self._APP:
         LOG.info("Got media key '%s'" % (what, ))
         tokens = self._bindings.get(what, [])
         if len(tokens) > 0:
             self._output.append(tuple(self._prefix + tokens))
Exemplo n.º 17
0
    def update_status(self, component, status):
        """
        @see Notifier.update_status()
        """
        # Sanity
        if component is None or status is None:
            return

        LOG.info("Component %s is now %s", component, status)
Exemplo n.º 18
0
    def _updater(self):
        """
        The method which will update the dongle.
        """
        # Some state variables
        i_mult = 0.0
        s_mult = 0.0
        o_mult = 0.0

        # And off we go!
        LOG.info("Started update thread")
        while self.is_running:
            # Don't busy-wait
            time.sleep(0.01)

            # What time is love?
            now = time.time()

            # How long since these components went non-idle
            i_since = now - self._input_time
            s_since = now - self._service_time
            o_since = now - self._output_time

            # Compute an level value from this
            level_scale = math.pi * 2
            i_level = 255 * (1 + math.sin(i_since * level_scale)) / 2
            s_level = 255 * (1 + math.sin(s_since * level_scale)) / 2
            o_level = 255 * (1 + math.sin(o_since * level_scale)) / 2

            # See what state we want these guys to be in. After 30s we figure
            # that the component is hung and turn it off.
            i_state = 1.0 if i_since < 30.0 else 0.0
            s_state = 1.0 if s_since < 30.0 else 0.0
            o_state = 1.0 if o_since < 30.0 else 0.0

            # Slide the multiplier accordingly
            f = 0.1
            i_mult = (1.0 - f) * i_mult + f * i_state
            s_mult = (1.0 - f) * s_mult + f * s_state
            o_mult = (1.0 - f) * o_mult + f * o_state

            # The RGB values
            r = int(max(0, min(255, o_level * o_mult)))
            g = int(max(0, min(255, s_level * s_mult)))
            b = int(max(0, min(255, i_level * i_mult)))

            # And set the dots
            for i in range(self._dots.n):
                new = [r, g, b, 1.0]
                if self._dots[i] != new:
                    self._dots[i] = new
            self._dots.show()

        # And we're done
        for i in range(self._dots.n):
            self._dots[i] = (0, 0, 0)
        LOG.info("Stopped update thread")
Exemplo n.º 19
0
 def stop(self):
     '''
     Stop all the notifiers.
     '''
     for notifier in self._notifiers:
         try:
             LOG.info("Stopping %s" % notifier)
             notifier.stop()
         except Exception as e:
             LOG.error("Failed to stop %s: %s" % (notifier, e))
Exemplo n.º 20
0
 def start(self):
     '''
     Start all the notifiers going.
     '''
     for notifier in self._notifiers:
         try:
             LOG.info("Starting %s" % notifier)
             notifier.start()
         except Exception as e:
             LOG.error("Failed to start %s: %s" % (notifier, e))
Exemplo n.º 21
0
    def _add_entry(self, entry):
        '''
        Add an entry to the index.

        @type  entry: _Entry
        @param entry:
            The entry to add to the index.
        '''
        # Ignore entries with no name
        if entry.name is None:
            return

        # We're adding it then
        LOG.info("Adding %s", entry.url)

        # Sanitise the strings thusly. This attempts to do a little data
        # cleaning along the way.
        def tidy(string):
            # Deal with empty strings
            string = _clean_string(string)
            if string is None:
                return None

            # Handle "_"s instead of spaces
            string = string.replace('_', ' ')
            string = _clean_string(string)
            if string is None:
                return None

            # Put ", The" back on the front
            if string.endswith(' The'):
                if string.endswith(', The'):
                    string = "The " + string[:-5]
                else:
                    string = "The " + string[:-4]

            # And, finally, make it all lower case so that we don't get fooled
            # by funny capitalisation
            string = string.lower()

            # And give it back
            return string.lower()

        # How we add the entry to an index
        def add(key, index, entry_):
            if key is not None:
                if key in index:
                    index[key].append(entry_)
                else:
                    index[key] = [entry_]

        # Add to the various indices
        add(tidy(entry.name), self._by_name, entry)
        add(tidy(entry.artist), self._by_artist, entry)
        add(tidy(entry.album), self._by_album, entry)
Exemplo n.º 22
0
    def interrupt(self):
        """
        @see Output.interrupt
        """
        self._interrupted = True

        if self._subproc is not None:
            # We do this by sending it a signal
            signal = subprocess.signal.SIGINT
            LOG.info("Sending %s to child process", signal)
            self._subproc.send_signal(signal)
Exemplo n.º 23
0
 def _decode_raw(self, data):
     '''
     @see AudioInput._decode_raw()
     '''
     audio = numpy.frombuffer(data, numpy.int16)
     words = self._model.stt(audio, self._rate)
     LOG.info("Got: %s" % (words, ))
     tokens = [
         Token(word.strip(), 1.0, True) for word in words.split(' ')
         if len(word.strip()) > 0
     ]
     return tokens
Exemplo n.º 24
0
    def _start(self):
        """
        @see Startable._start()
        """
        builder = clientbuilder.PydoraConfigFileBuilder('')
        if not builder.file_exists:
            raise ValueError("Unable to find config file; "
                             "have you run pydora-config yet?")

        self._pandora = builder.build()
        LOG.info("Configured as device %s", self._pandora.device)

        self._player = VLCPlayer(Callbacks(self), sys.stdin)
        self._player.start()
Exemplo n.º 25
0
    def evaluate(self, tokens):
        '''
        @see Service.evaluate()
        '''
        # Get stripped text, for matching
        text = ' '.join(to_letters(w) for w in self._words(tokens))

        # See if it matches an expected string
        for want in ('whats the time', 'what is the time', 'what time is it'):
            if text == want:
                LOG.info("Matched input on '%s'" % text)
                return _ClockHandler(self, tokens)

        # If we got here, then we didn't get an exact match
        return None
Exemplo n.º 26
0
    def _stop(self):
        '''
        Stop the system.
        '''
        # Stop the notifiers
        self._state.stop()

        # And the components
        for component in self._inputs + self._outputs + self._services:
            # Best effort, since we're likely shutting down
            try:
                LOG.info("Stopping %s" % (component, ))
                component.stop()

            except Exception as e:
                LOG.error("Failed to stop %s: %$s" % (component, e))
Exemplo n.º 27
0
 def evaluate(self, tokens):
     """
     @see Service.evaluate()
     """
     words = self._words(tokens)
     for (what, handler) in self._HANDLERS:
         for prefix in self._PREFICES:
             phrase = (prefix + what)
             try:
                 (s, e, _) = fuzzy_list_range(words, phrase)
                 if s == 0 and e == len(phrase):
                     return handler(self, tokens)
             except Exception as e:
                 LOG.debug("Failed to handle '%s': %s" %
                           (' '.join(words), e))
     return None
Exemplo n.º 28
0
    def _add_result(self, json_result):
        """
        Add in any result we have from the given JSON string.
        """
        result = json.loads(json_result)
        LOG.debug("Got %s" % json_result)

        # See what we got, if anything
        if 'result' in result:
            # A full result, which is the best
            self._results.extend(result['result'])
        elif 'text' in result:
            # A decoded text string
            for word in result['text'].split():
                if word:
                    self._results.append({'word': word, 'conf': 1.0})
Exemplo n.º 29
0
def main(config=None):
    '''
    Main entry point.
    '''
    if config is not None:
        try:
            with open(config) as fh:
                configuration = json.load(fh)
        except Exception as e:
            LOG.fatal("Failed to parse config file '%s': %s" % (config, e))
            sys.exit(1)
    else:
        configuration = CONFIG

    # And spawn it
    dexter = Dexter(configuration)
    dexter.run()
Exemplo n.º 30
0
    def __init__(self,
                 notifier,
                 rate=None,
                 wav_dir=None,
                 model=os.path.join(_MODEL_DIR, 'models.pbmm'),
                 scorer=os.path.join(_MODEL_DIR, 'models.scorer')):
        """
        @see AudioInput.__init__()

        :type  rate:
        :param rate:
            The override for the rate, if not the model's one.
        :type  wav_dir:
        :param wav_dir:
            Where to save the wave files, if anywhere.
        :type  model:
        :param model:
            The path to the DeepSpeech model file.
        :type  scorer:
        :param scorer:
            The path to the DeepSpeech scorer file.
        """
        # If these don't exist then DeepSpeech will segfault when inferring!
        if not os.path.exists(model):
            raise IOError("Not found: %s" % (model, ))

        # Load in and configure the model.
        LOG.info("Loading model from %s" % (model, ))
        self._model = Model(model)
        if os.path.exists(scorer):
            LOG.info("Loading scorer from %s" % (scorer, ))
            self._model.enableExternalScorer(scorer)

        # Handle any rate override
        if rate is None:
            rate = self._model.sampleRate()

        # Wen can now init the superclass
        super(DeepSpeechInput, self).__init__(notifier,
                                              format=pyaudio.paInt16,
                                              channels=1,
                                              rate=rate,
                                              wav_dir=wav_dir)

        # Where we put the stream context
        self._context = None