Bump Porcupine Python wrapper
This commit is contained in:
+61
-48
@@ -1,17 +1,12 @@
|
||||
#
|
||||
# Copyright 2018 Picovoice Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# You may not use this file except in compliance with the license. A copy of the license is located in the "LICENSE"
|
||||
# file accompanying this source.
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
#
|
||||
|
||||
import os
|
||||
@@ -20,7 +15,7 @@ from enum import Enum
|
||||
|
||||
|
||||
class Porcupine(object):
|
||||
"""Python binding for Picovoice's wake word detection (aka Porcupine) library."""
|
||||
"""Python binding for Picovoice's wake word detection (Porcupine) engine."""
|
||||
|
||||
class PicovoiceStatuses(Enum):
|
||||
"""Status codes corresponding to 'pv_status_t' defined in 'include/picovoice.h'"""
|
||||
@@ -29,11 +24,17 @@ class Porcupine(object):
|
||||
OUT_OF_MEMORY = 1
|
||||
IO_ERROR = 2
|
||||
INVALID_ARGUMENT = 3
|
||||
STOP_ITERATION = 4
|
||||
KEY_ERROR = 5
|
||||
INVALID_STATE = 6
|
||||
|
||||
_PICOVOICE_STATUS_TO_EXCEPTION = {
|
||||
PicovoiceStatuses.OUT_OF_MEMORY: MemoryError,
|
||||
PicovoiceStatuses.IO_ERROR: IOError,
|
||||
PicovoiceStatuses.INVALID_ARGUMENT: ValueError
|
||||
PicovoiceStatuses.INVALID_ARGUMENT: ValueError,
|
||||
PicovoiceStatuses.STOP_ITERATION: StopIteration,
|
||||
PicovoiceStatuses.KEY_ERROR: KeyError,
|
||||
PicovoiceStatuses.INVALID_STATE: ValueError,
|
||||
}
|
||||
|
||||
class CPorcupine(Structure):
|
||||
@@ -48,9 +49,9 @@ class Porcupine(object):
|
||||
keyword_file_paths=None,
|
||||
sensitivities=None):
|
||||
"""
|
||||
Loads Porcupine's shared library and creates an instance of wake word detection object.
|
||||
Constructor.
|
||||
|
||||
:param library_path: Absolute path to Porcupine's shared library.
|
||||
:param library_path: Absolute path to Porcupine's dynamic library.
|
||||
:param model_file_path: Absolute path to file containing model parameters.
|
||||
:param keyword_file_path: Absolute path to keyword file containing hyper-parameters. If not present then
|
||||
'keyword_file_paths' will be used.
|
||||
@@ -64,38 +65,38 @@ class Porcupine(object):
|
||||
"""
|
||||
|
||||
if not os.path.exists(library_path):
|
||||
raise IOError(f"Could not find Porcupine's library at '{library_path}'")
|
||||
raise IOError("could'nt find Porcupine's library at '%s'" % library_path)
|
||||
|
||||
library = cdll.LoadLibrary(library_path)
|
||||
|
||||
if not os.path.exists(model_file_path):
|
||||
raise IOError(f"Could not find model file at '{model_file_path}'")
|
||||
raise IOError("could'nt find model file at '%s'" % model_file_path)
|
||||
|
||||
if sensitivity is not None and keyword_file_path is not None:
|
||||
if not os.path.exists(keyword_file_path):
|
||||
raise IOError(f"Could not find keyword file at '{keyword_file_path}'")
|
||||
raise IOError("could'nt' find keyword file at '%s'" % keyword_file_path)
|
||||
keyword_file_paths = [keyword_file_path]
|
||||
|
||||
if not (0 <= sensitivity <= 1):
|
||||
raise ValueError('Sensitivity should be within [0, 1]')
|
||||
raise ValueError('sensitivity should be within [0, 1]')
|
||||
sensitivities = [sensitivity]
|
||||
elif sensitivities is not None and keyword_file_paths is not None:
|
||||
if len(keyword_file_paths) != len(sensitivities):
|
||||
raise ValueError("Different number of sensitivity and keyword file path parameters are provided.")
|
||||
raise ValueError("different number of sensitivity and keyword file path parameters are provided.")
|
||||
|
||||
for x in keyword_file_paths:
|
||||
if not os.path.exists(os.path.expanduser(x)):
|
||||
raise IOError(f"Could not find keyword file at '{x}'")
|
||||
raise IOError("could not find keyword file at '%s'" % x)
|
||||
|
||||
for x in sensitivities:
|
||||
if not (0 <= x <= 1):
|
||||
raise ValueError('Sensitivity should be within [0, 1]')
|
||||
raise ValueError('sensitivity should be within [0, 1]')
|
||||
else:
|
||||
raise ValueError("Sensitivity and/or keyword file path is missing")
|
||||
raise ValueError("sensitivity and/or keyword file path is missing")
|
||||
|
||||
self._num_keywords = len(keyword_file_paths)
|
||||
|
||||
init_func = library.pv_porcupine_multiple_keywords_init
|
||||
init_func = library.pv_porcupine_init
|
||||
init_func.argtypes = [
|
||||
c_char_p,
|
||||
c_int,
|
||||
@@ -107,44 +108,43 @@ class Porcupine(object):
|
||||
self._handle = POINTER(self.CPorcupine)()
|
||||
|
||||
status = init_func(
|
||||
model_file_path.encode(),
|
||||
model_file_path.encode('utf-8'),
|
||||
self._num_keywords,
|
||||
(c_char_p * self._num_keywords)(*[os.path.expanduser(x).encode() for x in keyword_file_paths]),
|
||||
(c_char_p * self._num_keywords)(*[os.path.expanduser(x).encode('utf-8') for x in keyword_file_paths]),
|
||||
(c_float * self._num_keywords)(*sensitivities),
|
||||
byref(self._handle))
|
||||
if status is not self.PicovoiceStatuses.SUCCESS:
|
||||
raise self._PICOVOICE_STATUS_TO_EXCEPTION[status]('Initialization failed')
|
||||
|
||||
self.process_func = library.pv_porcupine_multiple_keywords_process
|
||||
self.process_func.argtypes = [POINTER(self.CPorcupine), POINTER(c_short), POINTER(c_int)]
|
||||
self.process_func.restype = self.PicovoiceStatuses
|
||||
raise self._PICOVOICE_STATUS_TO_EXCEPTION[status]('initialization failed')
|
||||
|
||||
self._delete_func = library.pv_porcupine_delete
|
||||
self._delete_func.argtypes = [POINTER(self.CPorcupine)]
|
||||
self._delete_func.restype = None
|
||||
|
||||
self._sample_rate = library.pv_sample_rate()
|
||||
self.process_func = library.pv_porcupine_process
|
||||
self.process_func.argtypes = [POINTER(self.CPorcupine), POINTER(c_short), POINTER(c_int)]
|
||||
self.process_func.restype = self.PicovoiceStatuses
|
||||
|
||||
version_func = library.pv_porcupine_version
|
||||
version_func.argtypes = []
|
||||
version_func.restype = c_char_p
|
||||
self._version = version_func().decode('utf-8')
|
||||
|
||||
self._frame_length = library.pv_porcupine_frame_length()
|
||||
|
||||
@property
|
||||
def sample_rate(self):
|
||||
"""Audio sample rate accepted by Porcupine library."""
|
||||
self._sample_rate = library.pv_sample_rate()
|
||||
|
||||
return self._sample_rate
|
||||
def delete(self):
|
||||
"""Releases resources acquired by Porcupine's library."""
|
||||
|
||||
@property
|
||||
def frame_length(self):
|
||||
"""Number of audio samples per frame expected by C library."""
|
||||
|
||||
return self._frame_length
|
||||
self._delete_func(self._handle)
|
||||
|
||||
def process(self, pcm):
|
||||
"""
|
||||
Monitors incoming audio stream for given wake word(s).
|
||||
Processes a frame of the incoming audio stream and emits the detection result.
|
||||
|
||||
:param pcm: An array (or array-like) of consecutive audio samples. For more information regarding required audio
|
||||
properties (i.e. sample rate, number of channels encoding, and number of samples per frame) please refer to
|
||||
'include/pv_porcupine.h'.
|
||||
:param pcm: A frame of audio samples. The number of samples per frame can be attained by calling
|
||||
'.frame_length'. The incoming audio needs to have a sample rate equal to '.sample_rate' and be 16-bit
|
||||
linearly-encoded. Porcupine operates on single-channel audio.
|
||||
:return: For a single wake-word use cse True if wake word is detected. For multiple wake-word use case it
|
||||
returns the index of detected wake-word. Indexing is 0-based and according to ordering of input keyword file
|
||||
paths. It returns -1 when no keyword is detected.
|
||||
@@ -153,7 +153,7 @@ class Porcupine(object):
|
||||
result = c_int()
|
||||
status = self.process_func(self._handle, (c_short * len(pcm))(*pcm), byref(result))
|
||||
if status is not self.PicovoiceStatuses.SUCCESS:
|
||||
raise self._PICOVOICE_STATUS_TO_EXCEPTION[status]('Processing failed')
|
||||
raise self._PICOVOICE_STATUS_TO_EXCEPTION[status]()
|
||||
|
||||
keyword_index = result.value
|
||||
|
||||
@@ -162,7 +162,20 @@ class Porcupine(object):
|
||||
else:
|
||||
return keyword_index
|
||||
|
||||
def delete(self):
|
||||
"""Releases resources acquired by Porcupine's library."""
|
||||
@property
|
||||
def version(self):
|
||||
"""Getter for version"""
|
||||
|
||||
self._delete_func(self._handle)
|
||||
return self._version
|
||||
|
||||
@property
|
||||
def frame_length(self):
|
||||
"""Getter for number of audio samples per frame."""
|
||||
|
||||
return self._frame_length
|
||||
|
||||
@property
|
||||
def sample_rate(self):
|
||||
"""Audio sample rate accepted by Picovoice."""
|
||||
|
||||
return self._sample_rate
|
||||
|
||||
Reference in New Issue
Block a user