12 logger = logging.getLogger(
"snowboy")
13 logger.setLevel(logging.INFO)
14 TOP_DIR = os.path.dirname(os.path.abspath(__file__))
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")
22 """Ring buffer to hold audio from PortAudio""" 24 self.
_buf = collections.deque(maxlen=size)
27 """Adds data to the end of buffer""" 28 self._buf.extend(data)
31 """Retrieves data from the beginning of buffer and clears it""" 32 tmp = bytes(bytearray(self.
_buf))
38 """Simple callback function to play a wave file. By default it plays 41 :param str fname: wave file name 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)
54 stream_out.stop_stream()
61 Snowboy decoder to detect whether a keyword specified by `decoder_model` 62 exists in a microphone input stream. 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. 73 resource=RESOURCE_FILE,
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
82 tm = type(decoder_model)
83 ts = type(sensitivity)
85 decoder_model = [decoder_model]
87 sensitivity = [sensitivity]
88 model_str =
",".join(decoder_model)
91 resource_filename=resource.encode(), model_str=model_str.encode())
92 self.detector.SetAudioGain(audio_gain)
95 if len(decoder_model) > 1
and len(sensitivity) == 1:
97 if len(sensitivity) != 0:
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())
106 self.detector.NumChannels() * self.detector.SampleRate() * 5)
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)
118 def start(self, detected_callback=play_audio_file,
119 interrupt_check=
lambda:
False,
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. 129 :param detected_callback: a function or list of functions. The number of 130 items must match the number of models in 132 :param interrupt_check: a function that returns True if the main loop 134 :param float sleep_time: how much time in second every loop waits. 138 logger.debug(
"detect voice return")
141 tc = type(detected_callback)
143 detected_callback = [detected_callback]
144 if len(detected_callback) == 1
and self.
num_hotwords > 1:
148 "Error: hotwords in your models (%d) do not match the number of " \
149 "callbacks (%d)" % (self.
num_hotwords, len(detected_callback))
151 logger.debug(
"detecting...")
155 logger.debug(
"detect voice break")
157 data = self.ring_buffer.get()
159 time.sleep(sleep_time)
162 ans = self.detector.RunDetection(data)
164 logger.warning(
"Error initializing streams or reading audio data")
166 message =
"Keyword " + str(ans) +
" detected at time: " 167 message += time.strftime(
"%Y-%m-%d %H:%M:%S",
168 time.localtime(time.time()))
170 callback = detected_callback[ans-1]
171 if callback
is not None:
174 logger.debug(
"finished.")
178 Terminate audio stream. Users cannot call start() again to detect. 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)
def __init__(self, size=4096)
def start(self, detected_callback=play_audio_file, interrupt_check=lambda:False, sleep_time=0.03)