- 语音
- 音频
- 人工智能
- 嵌入式
【免费下载链接】porcupine
On-device wake word detection powered by deep learning
Porcupine 是 Picovoice 推出的端侧(on-device)唤醒词检测引擎,其 Web 绑定@picovoice/porcupine-web通过 WebAssembly 将深度学习驱动的唤醒词检测能力完整地带入浏览器,实现纯本地、离线、低延迟的"始终聆听"式语音交互。本文以 binding/web/README.md 为核心,结合仓库内 binding/web/src 源码、demo/web 示例与 binding/web/test 测试用例,系统讲解环境要求、模型加载、主线程/Worker 线程初始化、音频处理、自定义唤醒词与 API 在线训练,帮助开发者直接落地一个可运行的浏览器唤醒词检测应用。
Porcupine 唤醒词引擎概述
Porcupine 是一个高准确率且轻量的唤醒词引擎,用于构建"始终聆听"的语音应用。其核心特性包括:
- 私有且离线:音频数据完全在设备端处理,不需要上传云端;
- 准确:基于深度学习的检测模型;
- 资源高效:甚至可以运行在微控制器(MCU)上;
- 数据高效:唤醒词可以通过简单输入文本生成,无需数千小时的定制音频训练数据;
- 可扩展:可同时监听多个唤醒词 / 常驻语音命令;
- 跨平台:同一引擎提供 Android、iOS、Web、Node.js、Python、C、Flutter、React 等多种绑定。
在 Web 端,引擎以 WebAssembly 形式运行,仓库内对应两份 WASM 构建产物位于 lib/wasm:pv_porcupine_simd.wasm(SIMD 版)与pv_porcupine_pthread.wasm(SIMD + pthread 多线程版)。绑定层入口 binding/web/src/index.ts 在模块加载时通过Porcupine.setWasmSimd、Porcupine.setWasmPThread等静态方法将 WASM 文件注入,并在运行时依据浏览器能力自动选择版本。
兼容性与运行环境要求
浏览器兼容性
- Chrome / Edge
- Firefox
- Safari
SharedArrayBuffer 与响应头要求
Web 绑定依赖 SharedArrayBuffer,因此服务端必须在响应中包含以下两个头,否则浏览器无法启用多线程:
Cross-Origin-Opener-Policy: same-origin Cross-Origin-Embedder-Policy: require-corp仓库内的 demo/web/server.js 给出了一个完整的示例服务端:它对每个请求都写入上述两个响应头,监听127.0.0.1:5000并提供静态文件服务,可直接作为自定义服务器实现参考。
重要行为说明:不支持SharedArrayBuffer的浏览器,或未携带上述响应头的应用,会自动回退到标准ArrayBuffer——此时多线程处理将被禁用(对应源码逻辑见 binding/web/src/porcupine.ts 中sabDefined的判断:仅当SharedArrayBuffer存在且 device 非cpu:1时才选择 pthread 版 WASM)。
限制
- IndexedDB 是 Worker 线程使用
Porcupine的前提。不支持 IndexedDB 的浏览器(例如 Firefox 隐身模式)应改在主线程使用Porcupine。 - 多线程仅在 Web Worker 中启用。主线程初始化时,即使指定了多线程 device,引擎也会自动降级为
cpu:1并打印警告(见 binding/web/src/porcupine.ts 的_init逻辑)。
安装与 AccessKey
安装包
使用 Yarn:
yarn add @picovoice/porcupine-web或使用 npm:
npm install --save @picovoice/porcupine-web当前仓库 binding/web/package.json 中该包版本为4.0.1,声明engines.node >= 18,其运行时依赖为@picovoice/web-utils(负责模型加载、IndexedDB 存取与 AccessKey 校验等基础能力)。
AccessKey
Porcupine 在初始化时必须提供有效的 PicovoiceAccessKey,它相当于使用 Porcupine SDK 的凭据,请务必保密。可免费在 Picovoice Console 注册/登录后获取。源码中Porcupine.create会先通过isAccessKeyValid校验 AccessKey,非法时抛出PorcupineInvalidArgumentError(见 binding/web/src/porcupine.ts 与 binding/web/test/porcupine.test.ts 中should be able to handle invalid access key用例)。
模型文件的两种加载方式
参数模型文件(.pv)与自定义唤醒词文件(.ppn)均可通过两种方式提供给引擎:Public Directory(公开目录)或Base64。
Public Directory(公开目录)
注意:由于现代浏览器对 file:// URL 的限制,该方法必须通过服务器托管才能工作。
该方法从公开目录 fetch 模型文件并喂给 Porcupine。将模型文件复制到公开目录:
cp ${PORCUPINE_MODEL_FILE} ${PATH_TO_PUBLIC_DIRECTORY}仓库内默认的英语参数模型位于 lib/common/porcupine_params.pv,可将其复制到你的静态资源目录。同一流程也适用于自定义唤醒词(.ppn)文件。
Base64
注意:该方法无需托管服务器即可工作,但会使模型文件体积增大约 33%。
该方法使用模型文件的 base64 字符串喂给 Porcupine。使用内置脚本pvbase64将模型文件转换为 base64:
npx pvbase64 -i ${MODEL_FILE} -o ${OUTPUT_DIRECTORY}/${MODEL_NAME}.js输出是一个 JS 文件,可在项目的任意文件中 import。查看pvbase64的详细用法:
npx pvbase64 -h同一流程同样适用于自定义唤醒词(.ppn)文件,例如:
npx pvbase64 -i ${KEYWORD_FILE}.ppn -o ${KEYWORD_BASE64}.js -n ${KEYWORD_BASE64_VAR_NAME}其中-n用于指定生成的 JS 变量名。
Porcupine 模型配置(.pv 参数模型)
Porcupine 会将参数模型文件(.pv)保存并缓存在 IndexedDB中,供 WebAssembly 使用。模型配置对象如下:
// Model (.pv) const porcupineModel = { publicPath: ${MODEL_RELATIVE_PATH}, // or base64: ${MODEL_BASE64_STRING}, // Optional customWritePath: 'custom_model', forceWrite: true, version: 1, }字段说明(与源码 binding/web/src/porcupine.ts 中create的参数注释一致):
publicPath:模型文件相对于公开目录的路径;base64:模型的 base64 字符串;customWritePath:模型在 IndexedDB 中的自定义存储路径/键名。设置为不同名称即可在多个Porcupine实例间持有多个模型;未设置时默认写入porcupine_model;forceWrite:即使存储中已存在该模型也强制重新写入,置为true可覆盖;version:模型版本号。当模型文件发生变化时应递增版本号,以强制刷新缓存的模型。
关键规则:base64与publicPath二者必须设置其一才能实例化 Porcupine;若两者都设置,Porcupine 将优先使用base64模型。
初始化 Porcupine(主线程与 Worker 线程)
检测回调与错误回调
创建keywordDetectionCallback函数以接收引擎检测结果:
function keywordDetectionCallback(keyword) { console.log(`Porcupine detected keyword: ${keyword.label}`); }回调参数为PorcupineDetection对象,包含index(被检测关键词的索引)与label(被检测关键词的标签),见 binding/web/src/types.ts。
如需捕获错误,创建options对象并添加processErrorCallback:
function processErrorCallback(error: string) { ... } options.processErrorCallback = processErrorCallback;options还支持device参数,默认值为'best',可取值:
best:自动选择最合适的设备;gpu:使用第一个可用 GPU 设备;gpu:${GPU_INDEX}:指定索引的 GPU 设备;cpu:使用默认线程数在 CPU 上运行;cpu:${NUM_THREADS}:指定线程数(如cpu:1单线程、cpu:2双线程)。
从源码结构可以确认(见 binding/web/src/porcupine.ts 的
_init):主线程下若 device 为best或cpu:n(n>1),会自动降级并警告Multi-threading is not supported on main thread.,强制使用cpu:1;因此多线程加速必须配合 Worker 使用。
主线程初始化
const handle = await Porcupine.create( ${ACCESS_KEY}, PorcupineWeb.BuiltInKeyword.Porcupine, keywordDetectionCallback, porcupineModel, options // optional options );Worker 线程初始化
const handle = await PorcupineWorker.create( ${ACCESS_KEY}, PorcupineWeb.BuiltInKeyword.Porcupine, keywordDetectionCallback, porcupineModel, options // optional options );Worker 版本通过 binding/web/src/porcupine_worker.ts 内部创建 Web Worker,并向 binding/web/src/porcupine_worker_handler.ts 发送init/process/release三类命令消息;检测结果经porcupineDetection消息回传。测试用例 binding/web/test/porcupine.test.ts 对Porcupine与PorcupineWorker两组实例执行相同的初始化与处理断言,并验证sampleRate恒为16000、version为非空字符串。
内置唤醒词
关键字参数既可以是内置唤醒词,也可以是自定义模型对象。仓库 binding/web/src/built_in_keywords.ts 内置了 14 个可直接使用的唤醒词:
| 枚举值 | 唤醒词 | 枚举值 | 唤醒词 |
|---|---|---|---|
Alexa | Alexa | HeyGoogle | Hey Google |
Americano | Americano | HeySiri | Hey Siri |
Blueberry | Blueberry | Jarvis | Jarvis |
Bumblebee | Bumblebee | OkayGoogle | Okay Google |
Computer | Computer | Picovoice | Picovoice |
Grapefruit | Grapefruit | Porcupine | Porcupine |
Grasshopper | Grasshopper | Terminator | Terminator |
处理音频帧
检测结果通过上述keywordDetectionCallback异步返回。处理循环示例:
function getAudioData(): Int16Array { ... // function to get audio data return new Int16Array(); } for (; ;) { await handle.process(getAudioData()); // break on some condition }音频数据要求(见 binding/web/src/porcupine.ts 中process的注释与实现):
- 必须是
Int16Array(16 位线性编码 PCM),否则抛出PorcupineInvalidArgumentError; - 采样率可从实例属性
sampleRate获取(测试验证为 16000 Hz); - 每帧长度(采样点数)可从
frameLength获取; - 引擎只处理单声道音频。
底层调用链为_pv_porcupine_process:将 PCM 帧写入 WASM 堆(HEAP16),调用 C 层处理函数,再读取keywordIndex;若keywordIndex !== -1则触发检测回调。每次process由async-mutex的互斥锁串行化,避免并发处理导致的状态竞争。
资源清理:release 与 terminate
释放Porcupine或PorcupineWorker占用的资源:
await handle.release();release会调用 WASM 层的_pv_porcupine_delete并释放内部内存地址。对于PorcupineWorker,release还会关闭 Worker;如需立即终止 Worker 及其正在处理的所有请求,可调用:
await handle.terminate();terminate直接调用worker.terminate()(见 binding/web/src/porcupine_worker.ts)。
自定义唤醒词(.ppn)
在 Picovoice Console 中创建自定义唤醒词:训练并下载针对目标平台Web (WASM)的 Porcupine 唤醒词模型(.ppn)。
该模型文件可直接通过publicPath使用;若希望用 base64,则用内置pvbase64脚本转换.ppn:
npx pvbase64 -i ${KEYWORD_FILE}.ppn -o ${KEYWORD_BASE64}.js -n ${KEYWORD_BASE64_VAR_NAME}与参数模型(.pv)类似,唤醒词文件(.ppn)也会被保存到 IndexedDB供 WebAssembly 使用。每个关键词同样必须设置base64或publicPath之一,两者同时设置时优先使用base64。此外,必须提供一个任意的label用于在检测发生时标识该关键词。
// custom keyword (.ppn) const keywordModel = { publicPath: ${KEYWORD_RELATIVE_PATH}, // or base64: ${KEYWORD_BASE64_STRING}, label: ${KEYWORD_LABEL}, // Optional customWritePath: 'custom_keyword', forceWrite: true, version: 1, }其中sensitivity为可选项,取值范围[0, 1],默认0.5,用于在漏报率(miss rate)与误报率(false alarm)之间权衡;源码 binding/web/src/utils.ts 会校验敏感度必须为 [0,1] 区间内的数值,越界会直接抛出错误。测试用例也验证了sensitivity: -1会被拒绝。
初始化时传入关键词数组(可同时监听多个唤醒词):
const handle = await Porcupine.create( ${ACCESS_KEY}, [keywordModel], keywordDetectionCallback, porcupineModel, options );非英语语言支持
要检测非英语唤醒词,需要使用对应的语言参数模型(.pv)。仓库内 lib/common 提供了全部受支持语言的模型文件:porcupine_params.pv(英语)以及porcupine_params_de.pv(德语)、porcupine_params_es.pv(西班牙语)、porcupine_params_fr.pv(法语)、porcupine_params_it.pv(意大利语)、porcupine_params_ja.pv(日语)、porcupine_params_ko.pv(韩语)、porcupine_params_pt.pv(葡萄牙语)、porcupine_params_zh.pv(中文)。将对应语言的.pv复制到公开目录或转为 base64 后传入porcupineModel即可。
通过 API 在线训练唤醒词模型
除了在 Console 中训练,还可以直接通过 API 训练模型,无需打开控制台:
Porcupine.trainWakeWordFromPhrase( "${ACCESS_KEY}", // AccessKey obtained from Picovoice Console (https://console.picovoice.ai/) "${WRITE_PATH}", // Custom path/key used to store the trained model in IndexedDB "${LANGUAGE}", // Two-character language code "${PHRASE}") // Phrase to create a wake word from从源码 binding/web/src/porcupine.ts 的trainWakeWordFromPhrase实现可以确认以下细节:
writePath:训练好的模型在 IndexedDB 中的存储路径/键名;language:两位语言代码,当前白名单为['de', 'en', 'es', 'fr', 'it', 'ja', 'ko', 'pt'],非法语言会抛出PorcupineInvalidArgumentError;phrase:用于创建唤醒词的短语,不能为空且长度不能超过 64 个字符(PORCUPINE_PHRASE_MAX_LENGTH = 64);- 方法会向
https://rest.picovoice.ai/{language}/api/ppn发起 POST 请求,以 AccessKey 作为x-api-key头,平台固定为wasm; - 训练成功后模型写入 IndexedDB,返回的
PorcupineKeyword对象(含publicPath、customWritePath、label)可直接传给.create()使用。注意:返回对象须保持forceWrite: false或为空,Porcupine 才能正常初始化(避免强制覆盖缓存)。
受支持的语言与平台列表可参考 Picovoice 官方 Porcupine Model API 文档。
参考 Demo 与测试
仓库提供了可直接运行的 Web Demo(demo/web),它使用 IIFE 版库(HTML<script>标签方式),配合@picovoice/web-voice-processor采集麦克风音频并自动降采样到 16kHz 16-bit PCM,再交给 Porcupine Worker 检测;音频全程不离开浏览器。运行方式:
yarn yarn start ${LANGUAGE}或
npm install npm run start ${LANGUAGE}其中${LANGUAGE}为两位语言代码(如de、ko),不带参数运行可查看可用语言列表。启动后打开http://localhost:5000,输入 AccessKey、选择关键词并开始录音即可检测(demo/web/README.md)。Demo 的 scripts/porcupine.js 展示了PorcupineWorker.create与WebVoiceProcessor.subscribe的完整接线方式。
此外,binding/web/test/porcupine.test.ts 覆盖了 publicPath / base64 两种模型加载、非法 AccessKey、非法 base64、非法路径、非法敏感度、非法 device、UTF-8 路径以及单/多关键词检测等场景;binding/web/test/porcupine_train.test.ts 则验证了trainWakeWordFromPhrase训练成功后直接用返回关键词初始化的完整链路。这些测试既可作为功能清单,也可作为集成排错的对照参考。
结语
Porcupine Web 绑定把一套完整的离线唤醒词引擎压缩进了浏览器:借助 WebAssembly + IndexedDB 缓存 + SharedArrayBuffer 多线程(Worker 内),开发者只需一个 AccessKey、一个参数模型和一个关键词配置,即可在任意受支持浏览器中构建"始终聆听"的语音入口。无论是内置唤醒词快速验证、Console 自定义.ppn,还是通过trainWakeWordFromPhrase在线训练,binding/web 目录下的源码、demo/web 示例与 binding/web/test 测试都提供了可对照、可复用的落地参考。
- 语音
- 音频
- 人工智能
- 嵌入式
【免费下载链接】porcupine
On-device wake word detection powered by deep learning
相关推荐
Porcupine React 绑定完整实战:在 React 应用中集成端侧唤醒词检测
Porcupine React 绑定完整实战:在 React 应用中集成端侧唤醒词检测 Porcupine 是一款由 Picovoice 开发的轻量级、高精度的
语音音频人工智能嵌入式Porcupine Flutter 集成实战:在 Android/iOS 上实现离线唤醒词检测
Porcupine Flutter 集成实战:在 Android/iOS 上实现离线唤醒词检测 Porcupine 是一款基于深度学习的轻量级离线唤醒词引擎,本
语音音频人工智能嵌入式Porcupine Node.js 绑定(@picovoice/porcupine-node)完整使用指南:内置与自定义唤醒词检测实战
Porcupine Node.js 绑定(@picovoice/porcupine node)完整使用指南:内置与自定义唤醒词检测实战 本文围绕 Porcupi
语音音频人工智能嵌入式
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考