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