Ejemplo n.º 1
0
 def handle(self):
     """
     @see Handler.handle()`
     """
     LOG.info('Playing %s' % (self._what))
     self.service.play(self._token)
     return Result(self, '', False, True)
Ejemplo n.º 2
0
 def handle(self):
     '''
     @see Handler.handle()`
     '''
     LOG.info('Playing %s' % (self._what))
     self.service.play(self._filenames)
     return Result(self, '', False, True)
Ejemplo n.º 3
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)
Ejemplo n.º 4
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)
Ejemplo n.º 5
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)
Ejemplo n.º 6
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)
Ejemplo n.º 7
0
    def _build_from_dirname(self, dirname):
        '''
        Build an index based on the given directory root.

        @type  dirname: str
        @param dirname:
            The directory name to build from.
        '''
        # Walk the tree
        for (subdir, subdirs, files) in os.walk(dirname):
            LOG.info("Indexing %s", subdir)

            # Handle all the files which we can find
            for filename in files:
                try:
                    # Use mutagen to grab details
                    path = os.path.join(subdir, filename)
                    info = mutagen.File(path)
                    if isinstance(info, mutagen.mp3.MP3):
                        self._add_entry(AudioEntry.from_mp3(info))
                    elif isinstance(info, mutagen.flac.FLAC):
                        self._add_entry(AudioEntry.from_flac(info))
                    else:
                        LOG.debug("Ignoring %s", path)
                except Exception as e:
                    LOG.warning("Failed to index %s: %s", path, e)
Ejemplo n.º 8
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()
Ejemplo n.º 9
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)
Ejemplo n.º 10
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)
Ejemplo n.º 11
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))
Ejemplo n.º 12
0
    def _run(self):
        """
        The actual worker thread.
        """
        # 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

                # I've got something to say (it's better to burn out, than to
                # fade away...)
                command = '(SayText "%s")\n' % text
                LOG.info("Sending: %s" % command.strip())
                self._notify(Notifier.WORKING)
                self._subproc.stdin.write(command)
                self._subproc.stdin.flush()

                # Wait for an approximate amount of time that we think it will
                # take to say what we were told to. Yes, this isn't great but we
                # don't have a good way to tell when festival has done talking
                # owing to the fact it buffers its output when it's piping.
                while (not self._interrupted
                       and time.time() - start < len(text) * 0.04):
                    time.sleep(0.1)

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

            finally:
                self._notify(Notifier.IDLE)

        # Kill off the child
        try:
            if self._subproc is not None:
                self._subproc.terminate()
                self._subproc.communicate()
        except:
            pass
Ejemplo n.º 13
0
    def _decode(self):
        """
        @see AudioInput._decode()
        """
        if self._sckt is None:
            # No context means no tokens
            LOG.warning("Had no stream context to close")
            return []

        try:
            # Send the EOD token
            self._sckt.sendall(struct.pack('!q', -1))

            # Get back the result:
            #   8 bytes for the length
            #   data...
            LOG.info("Waiting for result...")
            length = b''
            while len(length) < 8:
                got = self._sckt.recv(8 - len(length))
                if len(got) == 0:
                    raise IOError("EOF in recv()")
                length += got
            (count, ) = struct.unpack("!q", length)

            # Read in the string
            LOG.info("Reading %d chars" % (count, ))
            result = b''
            while len(result) < count:
                got = self._sckt.recv(count - len(result))
                if len(got) == 0:
                    raise IOError("EOF in recv()")
                result += got
            result = result.decode()
            LOG.info("Result is: '%s'" % (result, ))

            # Convert to tokens
            tokens = [
                Token(word.strip(), 1.0, True) for word in result.split(' ')
                if word.strip() != ''
            ]
            return tokens

        except Exception as e:
            # Again, just grumble on exceptions
            LOG.info("Failed to do remote processing: %s" % e)
            return []

        finally:
            # Close it out, best effort
            try:
                LOG.info("Closing connection")
                self._sckt.shutdown(socket.SHUT_RDWR)
                self._sckt.close()
            except:
                pass
            finally:
                self._sckt = None
Ejemplo n.º 14
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))
Ejemplo n.º 15
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))
Ejemplo n.º 16
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")
Ejemplo 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)
Ejemplo n.º 18
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))
Ejemplo n.º 19
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))
Ejemplo n.º 20
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)
Ejemplo n.º 21
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)
Ejemplo n.º 22
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
Ejemplo n.º 23
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()
Ejemplo n.º 24
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
Ejemplo n.º 25
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))
Ejemplo n.º 26
0
    def set_volume(self, volume):
        '''
        Set the volume to a value between zero and eleven.

        @type  value: float
        @param value:
            The volume level to set. This should be between 0 and 11 inclusive.
        '''
        volume = float(value)

        if volume < 0 or volume > 11:
            raise ValueError("Volume out of [0..11] range: %s" % value)

        # Set as a fraction of 1
        v = (volume / 11)
        LOG.info("Setting volume to %0.2f" % v)
        pygame.mixer.music.set_volume(v)
Ejemplo n.º 27
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
Ejemplo n.º 28
0
    def run(self):
        '''
        The main worker.
        '''
        LOG.info("Starting the system")
        self._start()

        LOG.info("Entering main loop")
        while self._running:
            try:
                # What time is love?
                now = time.time()

                # Handle any events. First check to see if any time events are
                # pending and need to be scheduled.
                while len(self._timer_events) > 0 and \
                      self._timer_events[0].schedule_time <= now:
                    self._events.put(heapq.heappop(self._timer_events))

                # Now handle the actual events
                while not self._events.empty():
                    event = self._events.get()
                    try:
                        result = event.invoke()
                        if result is not None:
                            self._events.put(result)
                    except Exception as e:
                        LOG.error("Event %s raised exception: %s", event, e)

                # Loop over all the inputs and see if they have anything pending
                for input in self._inputs:
                    # Attempt a read, this will return None if there's nothing
                    # available
                    tokens = input.read()
                    if tokens is not None:
                        # Okay, we read something, attempt to handle it
                        LOG.info("Read from %s: %s" %
                                 (input, [str(t) for t in tokens]))
                        result = self._handle(tokens)

                        # If we got something back then give it back to the user
                        if result is not None:
                            self._respond(result)

                # Wait for a bit before going around again
                time.sleep(0.1)

            except KeyboardInterrupt:
                LOG.warning("KeyboardInterrupt received")
                break

        # We're out of the main loop, shut things down
        LOG.info("Stopping the system")
        self._stop()
Ejemplo n.º 29
0
    def evaluate(self, tokens):
        '''
        @see Service.evaluate()
        '''
        # Render to lower-case, for matching purposes.
        words = self._words(tokens)

        # Look for these types of queston
        prefices = (('what', 'is'), ('who', 'is'))
        for prefix in prefices:
            try:
                # Look for the prefix in the words
                index = list_index(words, prefix)

                # If we got here then we found the prefix. Strip that from the
                # query which we are about to make to Wikipedia.
                thing = ' '.join(words[index + len(prefix):]).strip()

                # Let's look to see if Wikipedia returns anything when we search
                # for this thing.
                belief = 0.5
                try:
                    self._notify(Notifier.ACTIVE)
                    results = [
                        result.lower().strip()
                        for result in wikipedia.search(thing)
                        if result is not None and len(result) > 0
                    ]
                    if thing in results:
                        belief = 1.0
                except Exception as e:
                    LOG.error("Failed to query Wikipedia for '%s': %s" %
                              (thing, e))
                finally:
                    self._notify(Notifier.IDLE)

                # Turn the words into a string for the handler.
                return _Handler(self, tokens, belief, thing)

            except Exception as e:
                LOG.info("%s not in %s: %s" % (prefix, words, e))

        # If we got here then it didn't look like a query for us.
        return None
Ejemplo n.º 30
0
    def _save_bytes(self, data):
        """
        Save the raw bytes to a wav file.

        :type  data: str
        :param data:
            The bytes to save out.
        """
        if self._wav_dir is None:
            return

        filename = os.path.join(self._wav_dir, "%d.wav" % time.time())
        LOG.info("Saving data as %s" % filename)
        with wave.open(filename, 'wb') as wf:
            wf.setnchannels(self._channels)
            wf.setsampwidth(self._width)
            wf.setframerate(self._rate)
            wf.writeframes(data)
            wf.close()