# # Copyright 2018-2023 Picovoice Inc. # # 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. # # 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 argparse import os import struct import wave from datetime import datetime import pvporcupine from pvrecorder import PvRecorder ACCESS_KEY = 'hqNqw85hkJRXVjEevwpkreB8n8so3w9JPQ27qnCR5qTH8a3+XnkZTA==' # pvporcupine.KEYWORDS # print(f"Keywords: {pvporcupine.KEYWORDS}") def main(): parser = argparse.ArgumentParser() parser.add_argument( '--access_key', default=ACCESS_KEY, help='AccessKey obtained from Picovoice Console (https://console.picovoice.ai/)') parser.add_argument( '--keywords', nargs='+', help='List of default keywords for detection. Available keywords: %s' % ', '.join( '%s' % w for w in sorted(pvporcupine.KEYWORDS)), # choices=sorted(pvporcupine.KEYWORDS), # default=['pico clock', 'picovoice', 'ok google', 'americano', 'hey barista', 'alexa', 'grasshopper', 'blueberry', 'hey siri', 'jarvis', 'porcupine', 'terminator', 'grapefruit', 'computer', 'hey google', 'bumblebee'], default=['可莉可莉'], metavar='') parser.add_argument( '--keyword_paths', default=[r"picovoice_models/可莉可莉_zh_raspberry-pi_v3_0_0.ppn"], nargs='+', help="Absolute paths to keyword model files. If not set it will be populated from `--keywords` argument") parser.add_argument( '--library_path', help='Absolute path to dynamic library. Default: using the library provided by `pvporcupine`') parser.add_argument( '--model_path', default=r"picovoice_models/porcupine_params_zh.pv", help='Absolute path to the file containing model parameters. ' 'Default: using the library provided by `pvporcupine`') parser.add_argument( '--sensitivities', nargs='+', help="Sensitivities for detecting keywords. Each value should be a number within [0, 1]. A higher " "sensitivity results in fewer misses at the cost of increasing the false alarm rate. If not set 0.5 " "will be used.", type=float, default=None) parser.add_argument('--audio_device_index', help='Index of input audio device.', type=int, default=-1) parser.add_argument('--output_path', help='Absolute path to recorded audio for debugging.', default=None) parser.add_argument('--show_audio_devices', action='store_true') args = parser.parse_args() if args.show_audio_devices: for i, device in enumerate(PvRecorder.get_available_devices()): print('Device %d: %s' % (i, device)) return if args.keyword_paths is None: if args.keywords is None: raise ValueError("Either `--keywords` or `--keyword_paths` must be set.") keyword_paths = [pvporcupine.KEYWORD_PATHS[x] for x in args.keywords] else: keyword_paths = args.keyword_paths # TODO for i, kw_path in enumerate(keyword_paths): if os.path.dirname(__file__) not in kw_path: keyword_paths[i] = os.path.join(os.path.abspath(os.path.dirname(__file__)), kw_path) args.model_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), args.model_path) print(f"keyword_paths: {keyword_paths}") print(f"model_path: {args.model_path}") if args.sensitivities is None: args.sensitivities = [0.9] * len(keyword_paths) if len(keyword_paths) != len(args.sensitivities): raise ValueError('Number of keywords does not match the number of sensitivities.') try: porcupine = pvporcupine.create( access_key=args.access_key, library_path=args.library_path, model_path=args.model_path, keyword_paths=keyword_paths, sensitivities=args.sensitivities) except pvporcupine.PorcupineInvalidArgumentError as e: print("One or more arguments provided to Porcupine is invalid: ", args) print(e) raise e except pvporcupine.PorcupineActivationError as e: print("AccessKey activation error") raise e except pvporcupine.PorcupineActivationLimitError as e: print("AccessKey '%s' has reached it's temporary device limit" % args.access_key) raise e except pvporcupine.PorcupineActivationRefusedError as e: print("AccessKey '%s' refused" % args.access_key) raise e except pvporcupine.PorcupineActivationThrottledError as e: print("AccessKey '%s' has been throttled" % args.access_key) raise e except pvporcupine.PorcupineError as e: print("Failed to initialize Porcupine") raise e keywords = list() for x in keyword_paths: keyword_phrase_part = os.path.basename(x).replace('.ppn', '').split('_') if len(keyword_phrase_part) > 6: keywords.append(' '.join(keyword_phrase_part[0:-6])) else: keywords.append(keyword_phrase_part[0]) print('Porcupine version: %s' % porcupine.version) recorder = PvRecorder( frame_length=porcupine.frame_length, device_index=args.audio_device_index) recorder.start() wav_file = None if args.output_path is not None: wav_file = wave.open(args.output_path, "w") wav_file.setnchannels(1) wav_file.setsampwidth(2) wav_file.setframerate(16000) print('Listening ... (press Ctrl+C to exit)') try: while True: pcm = recorder.read() result = porcupine.process(pcm) if wav_file is not None: wav_file.writeframes(struct.pack("h" * len(pcm), *pcm)) if result >= 0: print('[%s] Detected %s' % (str(datetime.now()), keywords[result])) except KeyboardInterrupt: print('Stopping ...') finally: recorder.delete() porcupine.delete() if wav_file is not None: wav_file.close() if __name__ == '__main__': main()