snowboydecoder.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 import collections
4 import pyaudio
5 import snowboydetect
6 import time
7 import wave
8 import os
9 import logging
10 
11 logging.basicConfig()
12 logger = logging.getLogger("snowboy")
13 logger.setLevel(logging.INFO)
14 TOP_DIR = os.path.dirname(os.path.abspath(__file__))
15 
16 RESOURCE_FILE = os.path.join(TOP_DIR, "resources/common.res")
17 DETECT_DING = os.path.join(TOP_DIR, "resources/ding.wav")
18 DETECT_DONG = os.path.join(TOP_DIR, "resources/dong.wav")
19 
20 
21 class RingBuffer(object):
22  """Ring buffer to hold audio from PortAudio"""
23  def __init__(self, size = 4096):
24  self._buf = collections.deque(maxlen=size)
25 
26  def extend(self, data):
27  """Adds data to the end of buffer"""
28  self._buf.extend(data)
29 
30  def get(self):
31  """Retrieves data from the beginning of buffer and clears it"""
32  tmp = bytes(bytearray(self._buf))
33  self._buf.clear()
34  return tmp
35 
36 
37 def play_audio_file(fname=DETECT_DING):
38  """Simple callback function to play a wave file. By default it plays
39  a Ding sound.
40 
41  :param str fname: wave file name
42  :return: None
43  """
44  ding_wav = wave.open(fname, 'rb')
45  ding_data = ding_wav.readframes(ding_wav.getnframes())
46  audio = pyaudio.PyAudio()
47  stream_out = audio.open(
48  format=audio.get_format_from_width(ding_wav.getsampwidth()),
49  channels=ding_wav.getnchannels(),
50  rate=ding_wav.getframerate(), input=False, output=True)
51  stream_out.start_stream()
52  stream_out.write(ding_data)
53  time.sleep(0.2)
54  stream_out.stop_stream()
55  stream_out.close()
56  audio.terminate()
57 
58 
59 class HotwordDetector(object):
60  """
61  Snowboy decoder to detect whether a keyword specified by `decoder_model`
62  exists in a microphone input stream.
63 
64  :param decoder_model: decoder model file path, a string or a list of strings
65  :param resource: resource file path.
66  :param sensitivity: decoder sensitivity, a float of a list of floats.
67  The bigger the value, the more senstive the
68  decoder. If an empty list is provided, then the
69  default sensitivity in the model will be used.
70  :param audio_gain: multiply input volume by this factor.
71  """
72  def __init__(self, decoder_model,
73  resource=RESOURCE_FILE,
74  sensitivity=[],
75  audio_gain=1):
76 
77  def audio_callback(in_data, frame_count, time_info, status):
78  self.ring_buffer.extend(in_data)
79  play_data = chr(0) * len(in_data)
80  return play_data, pyaudio.paContinue
81 
82  tm = type(decoder_model)
83  ts = type(sensitivity)
84  if tm is not list:
85  decoder_model = [decoder_model]
86  if ts is not list:
87  sensitivity = [sensitivity]
88  model_str = ",".join(decoder_model)
89 
91  resource_filename=resource.encode(), model_str=model_str.encode())
92  self.detector.SetAudioGain(audio_gain)
93  self.num_hotwords = self.detector.NumHotwords()
94 
95  if len(decoder_model) > 1 and len(sensitivity) == 1:
96  sensitivity = sensitivity*self.num_hotwords
97  if len(sensitivity) != 0:
98  assert self.num_hotwords == len(sensitivity), \
99  "number of hotwords in decoder_model (%d) and sensitivity " \
100  "(%d) does not match" % (self.num_hotwords, len(sensitivity))
101  sensitivity_str = ",".join([str(t) for t in sensitivity])
102  if len(sensitivity) != 0:
103  self.detector.SetSensitivity(sensitivity_str.encode())
104 
106  self.detector.NumChannels() * self.detector.SampleRate() * 5)
107  self.audio = pyaudio.PyAudio()
108  self.stream_in = self.audio.open(
109  input=True, output=False,
110  format=self.audio.get_format_from_width(
111  self.detector.BitsPerSample() / 8),
112  channels=self.detector.NumChannels(),
113  rate=self.detector.SampleRate(),
114  frames_per_buffer=2048,
115  stream_callback=audio_callback)
116 
117 
118  def start(self, detected_callback=play_audio_file,
119  interrupt_check=lambda: False,
120  sleep_time=0.03):
121  """
122  Start the voice detector. For every `sleep_time` second it checks the
123  audio buffer for triggering keywords. If detected, then call
124  corresponding function in `detected_callback`, which can be a single
125  function (single model) or a list of callback functions (multiple
126  models). Every loop it also calls `interrupt_check` -- if it returns
127  True, then breaks from the loop and return.
128 
129  :param detected_callback: a function or list of functions. The number of
130  items must match the number of models in
131  `decoder_model`.
132  :param interrupt_check: a function that returns True if the main loop
133  needs to stop.
134  :param float sleep_time: how much time in second every loop waits.
135  :return: None
136  """
137  if interrupt_check():
138  logger.debug("detect voice return")
139  return
140 
141  tc = type(detected_callback)
142  if tc is not list:
143  detected_callback = [detected_callback]
144  if len(detected_callback) == 1 and self.num_hotwords > 1:
145  detected_callback *= self.num_hotwords
146 
147  assert self.num_hotwords == len(detected_callback), \
148  "Error: hotwords in your models (%d) do not match the number of " \
149  "callbacks (%d)" % (self.num_hotwords, len(detected_callback))
150 
151  logger.debug("detecting...")
152 
153  while True:
154  if interrupt_check():
155  logger.debug("detect voice break")
156  break
157  data = self.ring_buffer.get()
158  if len(data) == 0:
159  time.sleep(sleep_time)
160  continue
161 
162  ans = self.detector.RunDetection(data)
163  if ans == -1:
164  logger.warning("Error initializing streams or reading audio data")
165  elif ans > 0:
166  message = "Keyword " + str(ans) + " detected at time: "
167  message += time.strftime("%Y-%m-%d %H:%M:%S",
168  time.localtime(time.time()))
169  logger.info(message)
170  callback = detected_callback[ans-1]
171  if callback is not None:
172  callback()
173 
174  logger.debug("finished.")
175 
176  def terminate(self):
177  """
178  Terminate audio stream. Users cannot call start() again to detect.
179  :return: None
180  """
181  self.stream_in.stop_stream()
182  self.stream_in.close()
183  self.audio.terminate()
def play_audio_file(fname=DETECT_DING)
def __init__(self, decoder_model, resource=RESOURCE_FILE, sensitivity=[], audio_gain=1)
interrupt_check
Definition: demo.py:32
def start(self, detected_callback=play_audio_file, interrupt_check=lambda:False, sleep_time=0.03)


dialogflow_ros
Author(s): Anas Abou Allaban
autogenerated on Mon Jun 10 2019 13:02:59