Kitt-AI / snowboy

Future versions with model training module will be maintained through a forked version here: https://github.com/seasalt-ai/snowboy
Other
3.08k stars 997 forks source link

Code hanging if I remove logging. #286

Open AMEvers opened 6 years ago

AMEvers commented 6 years ago

I'm not sure if this warrants an issue or not. I've been modifying snowboy to, on hotword detection, start buffering audio data that I would like to write to a .wav. I was having issues when I modified ring buffer where it stopped detecting when I stopped talking. Weirdly, when I added a debug statement, it suddenly worked fine. Here's the modified code.


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, erase):
        """Retrieves data from the beginning of buffer and clears it"""
        tmp = bytes(bytearray(self._buf))
        if erase is True:
            self._buf.clear()
        return tmp
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...")
        triggered = False

        while True:
            if interrupt_check():
                logger.debug("detect voice break")
                break

            if triggered is False:
                data = self.ring_buffer.get(True)
            else:
                data = self.ring_buffer.get(False)

            if len(data) == 0:
                time.sleep(sleep_time)
                continue

            ans = self.detector.RunDetection(data)

            #message = str(ans)
            #logger.info(message)

            if ans == -1:
                logger.warning("Error initializing streams or reading audio data")
            elif ans > 0:
                triggered = True

                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()
            elif ans == -2 and triggered is True:
                triggered = False
                data = self.ring_buffer.get(True)
                message = "Done recording"
                logger.info(message)

        logger.debug("finished.")

If I uncomment ` #message = str(ans)

logger.info(message)`

It works fine, and if I comment it out, I never trigger my last elif.

Also, if you can offer me any advice on how to actually store data as a .wav so I can hear what was actually said after the hotword? I've been struggling to get the data to save in a readable .wav format.

chenguoguo commented 6 years ago

You can write the audio as raw data, then use sox to convert it, something like:

sox -r 16000 -c 1 -b 16 -e signed-integer -t raw your_audio.raw -t wav your_audio.wav

Otherwise, you can also try to use Python packages that support wave format audio.

AMEvers commented 6 years ago

Do you have any idea why adding a logging message is affecting whether the code detects the end of speech? It just never gets past elif ans == -2 and triggered is true and it's really weird that something like a log message would affect it.

chenguoguo commented 6 years ago

I do not have a clue. These two lines of logger info were not in our original example.