Testosterone and sensitivity.
[kiosk.git] / listen.py
1 #!/usr/bin/env python3
2
3 import logging
4 import os
5 import struct
6
7 import pvporcupine
8 import pyaudio
9 import speech_recognition as sr
10 from pyutils import logging_utils
11
12 import kiosk_secrets as secrets
13
14 logger = logging.getLogger(__name__)
15
16
17 class HotwordListener(object):
18     def __init__(
19         self,
20         command_queue,
21         keyword_paths,
22         sensitivities,
23         input_device_index=None,
24 #        library_path=pvporcupine.LIBRARY_PATH,
25 #        model_path=pvporcupine.MODEL_PATH,
26     ):
27         self._queue = command_queue
28 #        self._library_path = library_path
29 #        self._model_path = model_path
30         self._keyword_paths = keyword_paths
31         self._sensitivities = sensitivities
32         self._input_device_index = input_device_index
33
34     @logging_utils.LoggingContext(logger, prefix="listener:")
35     def listen_forever(self):
36         keywords = list()
37         for x in self._keyword_paths:
38             keywords.append(os.path.basename(x).replace(".ppn", "").split("_")[0])
39
40         porcupine = None
41         pa = None
42         audio_stream = None
43         try:
44             porcupine = pvporcupine.create(
45                 keyword_paths=self._keyword_paths,
46                 sensitivities=self._sensitivities,
47                 access_key=secrets.pvporcupine_key,
48             )
49             recognizer = sr.Recognizer()
50             pa = pyaudio.PyAudio()
51
52             audio_stream = pa.open(
53                 rate=porcupine.sample_rate,
54                 channels=1,
55                 format=pyaudio.paInt16,
56                 input=True,
57                 frames_per_buffer=porcupine.frame_length,
58                 input_device_index=self._input_device_index,
59             )
60
61             logger.info("Listening {")
62             for keyword, sensitivity in zip(keywords, self._sensitivities):
63                 logger.info("  %s (%.2f)" % (keyword, sensitivity))
64             logger.info("}")
65
66             while True:
67                 raw = audio_stream.read(
68                     porcupine.frame_length, exception_on_overflow=False
69                 )
70                 pcm = struct.unpack_from("h" * porcupine.frame_length, raw)
71                 result = porcupine.process(pcm)
72                 if result >= 0:
73                     cmd = "aplay /var/www/kiosk/attention.wav"
74                     logger.info(
75                         "Running %s (attention tone) because I heard the wake-word", cmd
76                     )
77                     os.system(cmd)
78                     logger.debug(
79                         ">>>>>>>>>>>>> Detected wakeword %s" % keywords[result]
80                     )
81                     raw = bytearray()
82                     for i in range(
83                         0, int(porcupine.sample_rate / porcupine.frame_length * 4)
84                     ):
85                         raw += audio_stream.read(
86                             porcupine.frame_length, exception_on_overflow=False
87                         )
88                     logger.debug(
89                         f">>>>>>>>>>>>>> Recognizing command... {len(raw)} bytes"
90                     )
91                     speech = sr.AudioData(
92                         frame_data=bytes(raw),
93                         sample_rate=porcupine.sample_rate,
94                         sample_width=2,  # 16 bits
95                     )
96                     command = recognizer.recognize_google(speech)
97                     logger.debug(">>>>>>>>>>>>> Google says command was %s" % command)
98                     logger.info("Enqueued command=%s", command)
99                     self._queue.put(command)
100
101         except Exception:
102             logger.exception("Stopping listener because of unexpected exception!")
103
104         except KeyboardInterrupt:
105             logger.exception("Stopping listener because of ^C!")
106
107         finally:
108             logger.debug("Cleaning up... one sec...")
109             if porcupine is not None:
110                 porcupine.delete()
111
112             if audio_stream is not None:
113                 audio_stream.close()
114
115             if pa is not None:
116                 pa.terminate()
117
118     @classmethod
119     def show_audio_devices(cls):
120         fields = ("index", "name", "defaultSampleRate", "maxInputChannels")
121         pa = pyaudio.PyAudio()
122         for i in range(pa.get_device_count()):
123             info = pa.get_device_info_by_index(i)
124             print(", ".join("'%s': '%s'" % (k, str(info[k])) for k in fields))
125         pa.terminate()
126
127
128 def main():
129     keyword_paths = [pvporcupine.KEYWORD_PATHS[x] for x in ["blueberry", "bumblebee"]]
130     sensitivities = [0.85, 0.95]
131     HotwordListener(
132         [],
133         keyword_paths,
134         sensitivities,
135     ).listen_forever()
136
137
138 if __name__ == "__main__":
139     main()