更新记录

1.0.0(2026-05-30)

  • 首发版本,集成 sherpa-onnx 端侧语音 AI Runtime
  • 支持 12 项语音能力:离线 ASR、实时 ASR、VAD、TTS、KWS、Speaker Embedding、Speaker Segmentation、标点恢复、语种识别、音频标签、语音增强、声源分离
  • 提供完整测试页面,覆盖所有功能入口和参数配置
  • 支持 static 资源目录和绝对路径两种模型加载方式
  • 支持 iOS CoreML 推理加速

平台兼容性

uni-app(5.07)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
× × × × ×
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
× × × × × × × × × × × ×

uni-app x(5.07)

Chrome Safari Android iOS 鸿蒙 微信小程序
× × × ×

em-sherpa-onnx — 端侧离线语音 AI 引擎

概述

em-sherpa-onnx 是基于 sherpa-onnx 的 UniApp X 本地语音 AI Runtime 插件,面向端侧离线语音场景。所有推理在设备本地完成,无需网络连接,数据不出设备。

功能矩阵

功能 插件 type 说明
离线语音识别 (ASR / STT) asr-offline 识别音频文件中的语音内容,支持多语种
实时语音识别 asr-online 通过麦克风流式获取中间识别结果
语音活动检测 (VAD) vad 检测音频中人声起止时间,用于语音端点检测
语音合成 (TTS) tts 将文本合成为自然语音,支持离线和流式
关键词唤醒 (KWS) kws 监听麦克风,检测预设关键词并触发事件
说话人嵌入 speaker-embedding 提取说话人声纹特征,用于注册/识别/验证
说话人分段 speaker-segmentation 多说话人音频的分段标注(Diarization)
标点恢复 punctuation 为无标点文本添加标点符号
语种识别 language-id 识别音频所属语种
音频标签 audio-tagging 识别音频中的环境声音或事件标签
语音增强 enhancement 去除背景噪声,提升语音清晰度
声源分离 source-separation 将混合音频分离为人声和伴奏等独立音轨

应用场景

  • 语音笔记 & 会议转写:离线 ASR 实时转写本地录音,支持中英粤日韩等多语种
  • 语音助手 & 智能家居:KWS 关键词唤醒 + ASR 语音指令理解,完全离线保护隐私
  • 语音合成播报:TTS 将通知、新闻、导航播报等文本转为语音播放
  • 声纹登录 & 验证:Speaker Embedding 注册用户声纹,实现免密码身份确认
  • 多说话人会议标注:Speaker Segmentation 区分不同说话人,生成带标签的转写文本
  • 音频内容审核 & 分析:Audio Tagging 识别音频中的枪声、警报、猫叫等事件标签
  • 通话降噪 & 录音增强:Enhancement 实时去除环境噪声,改善远端通话质量

引入

import {
  getRuntimeInfo,
  loadModel,
  recognizeFile,
  synthesize,
  startStreamingAsr,
  getStreamEventChannel,
} from "@/uni_modules/em-sherpa-onnx";

模型下载

插件不内置大模型。业务侧应将模型放在 static 目录,或下载到本地绝对路径后传给插件。

官方下载入口

推荐测试模型(全功能清单)

为覆盖插件所有 12 项能力,建议按功能分别下载以下模型:

功能 推荐模型 插件 type 下载地址
离线 ASR(中英粤日韩) SenseVoice int8 asr-offline 下载
实时 ASR(中英) Streaming Zipformer asr-online 下载
VAD Silero VAD vad 下载
TTS(中英) VITS Melo TTS tts 下载
KWS Zipformer KWS kws 下载
Speaker Embedding 3D Speaker ERES2Net speaker-embedding 下载
Speaker Segmentation PyAnnote Segmentation 3.0 speaker-segmentation 下载
标点恢复 CT-Transformer punctuation 下载
语种识别 Whisper Tiny language-id 下载
音频标签 Zipformer Audio Tagging audio-tagging 下载
语音增强 GTCRN enhancement 下载
声源分离 Spleeter 2stems source-separation 下载

完整模型清单和下载脚本参考 docs/model-downloads.md

下载 & 放置示例

# 创建模型目录
mkdir -p uni_sherpa/static/assets/models

# 下载 SenseVoice ASR 模型
curl -L -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17.tar.bz2
tar xvf sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17.tar.bz2
mv sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17 sense-voice-zh-en-ja-ko-yue-int8-2024-07-17
rm sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17.tar.bz2

# 目录结构
# uni_sherpa/static/assets/models/
#   sense-voice-zh-en-ja-ko-yue-int8-2024-07-17/
#     model.int8.onnx
#     tokens.txt

使用说明

1. 运行时管理

加载模型

模型可放置在 static 资源目录或设备沙盒绝对路径下。

// 从 static 资源加载
await loadModel({
  type: "asr-offline",
  source: "static",
  modelDir: "static/assets/models/sense-voice-zh-en-ja-ko-yue-int8-2024-07-17",
  modelId: "asr-main",
  numThreads: 2,
  provider: "cpu",
});

// 从设备绝对路径加载
await loadModel({
  type: "asr-offline",
  source: "absolute",
  modelDir:
    "/storage/emulated/0/models/sense-voice-zh-en-ja-ko-yue-int8-2024-07-17",
  modelId: "asr-main",
  numThreads: 2,
  provider: "cpu",
});

参数说明:

参数 类型 必填 说明
type string 模型类型:asr-offlineasr-onlinevadttskwsspeaker-embeddingspeaker-segmentationpunctuationlanguage-idaudio-taggingenhancementsource-separation
source 'static' \| 'absolute' static 从 App 资源目录读取;absolute 从设备文件系统读取
modelDir string 模型目录路径(静态资源或绝对路径)
modelId string 自定义模型标识,后续操作通过此 ID 引用模型;不传则自动生成
numThreads number 推理线程数(默认 2)
provider 'cpu' \| 'coreml' 推理加速后端,iOS 支持 coreml,Android 仅 cpu

查看运行时信息

const info = await getRuntimeInfo();
console.log(info.version, info.platform, JSON.stringify(info.models));

查看模型能力

const capabilities = await getModelCapabilities("asr-main");
console.log(JSON.stringify(capabilities));
// 输出示例:
// {
//   "type": "asr-offline",
//   "engine": "sense-voice",
//   "sampleRate": 16000,
//   "supportsOffline": true,
//   "supportsStreaming": false,
//   "preferredLanguage": "auto",
//   "languages": ["zh", "yue", "en", "ja", "ko"]
// }

释放资源

// 释放所有模型和实时任务
await releaseAll();

完整示例

参考 pages/runtime/runtime.uvue — 提供模型类型选择、路径输入、provider 切换、加载/释放按钮和输出面板。典型流程:

  1. 选择模型类型(如 asr-offline
  2. 填写模型目录路径
  3. 点击 Load 按钮加载模型
  4. 查看 capability 输出
  5. 测试完成后点击 Release 释放

2. 离线语音识别 (ASR / STT)

// 先加载模型
await loadModel({
  type: "asr-offline",
  source: "static",
  modelDir: "static/assets/models/sense-voice-zh-en-ja-ko-yue-int8-2024-07-17",
  modelId: "asr-main",
  engine: "sense-voice",
  numThreads: 2,
  provider: "cpu",
});

// 识别文件
const result = await recognizeFile({
  modelId: "asr-main",
  path: "/path/to/audio.wav",
  language: "auto", // auto | zh | yue | en | ja | ko
  useItn: true, // 启用标点逆文本正则化
});

console.log(result.text); // 识别结果文字

参数说明:

参数 类型 必填 说明
modelId string 已加载的 ASR 模型 ID
path string 音频文件路径(16kHz 单声道 wav 最佳)
language string 语种指定:auto 自动检测,或 zh/yue/en/ja/ko
useItn boolean 是否启用标点恢复和逆文本正则化(默认 false)

完整示例

参考 pages/asr/asr.uvue — 支持:

  • 加载 ASR 模型:选择路径后点击 Load ASR
  • 单文件识别:填入音频路径后点击 ASR File
  • 多语言样例:点击 ASR Samples 依次识别中/粤/英/日/韩测试音频
  • 快捷语种切换:自动填入对应语言的测试音频路径和 language 参数

3. 实时语音识别 (Streaming ASR)

// 加载实时 ASR 模型
await loadModel({
  type: "asr-online",
  source: "static",
  modelDir: "static/assets/models/asr-streaming-zh-en",
  modelId: "asr-stream",
  numThreads: 2,
  provider: "cpu",
  config: {
    modelType: "zipformer", // 模型架构
    encoder: "encoder-epoch-99.onnx", // 文件名映射
    decoder: "decoder-epoch-99.onnx",
    joiner: "joiner-epoch-99.onnx",
    tokens: "tokens.txt",
  },
});

// 订阅实时事件
const channel = getStreamEventChannel();
uni.$on(channel, (event: string) => {
  const data = JSON.parse(event);
  // data.type: 'asr-online-partial' | 'asr-online-final' | 'asr-online-error' | ...
  // data.data: 对应结果
  console.log(data.type, data.data);
});

// 启动麦克风流式识别
const taskId = await startStreamingAsr({
  modelId: "asr-stream",
  language: "zh",
});

// 取消任务
await cancelTask(taskId);

完整示例

参考 pages/stream/stream.uvue — 提供:

  • 加载实时 ASR 模型(支持 Zipformer / Paraformer 架构)
  • 启动流式识别:打开麦克风并实时返回 partial / final 结果
  • 停止 & 取消:结束当前实时任务并拉取已缓存事件

4. 语音活动检测 (VAD)

// 加载 VAD 模型(单文件模型)
await loadModel({
  type: "vad",
  source: "static",
  modelDir: "static/assets/models/silero-vad",
  modelId: "vad-main",
  numThreads: 1,
  provider: "cpu",
});

// 文件级 VAD 检测
const segments = await vadFile({
  modelId: "vad-main",
  path: "/path/to/audio.wav",
});
// segments: Array<{ start: number, end: number }> 单位秒

// 麦克风实时 VAD(需订阅事件通道)
const taskId = await startVadStream({ modelId: "vad-main" });

VAD + ASR 联动

await loadModel({ type: 'vad', modelId: 'vad-main', ... })
await loadModel({ type: 'asr-offline', modelId: 'asr-main', ... })

const taskId = await startVadAsr({
  vadModelId: 'vad-main',
  asrModelId: 'asr-main',
  language: 'auto'
})
// 事件通道返回 vad-asr 事件,包含语音段起止时间和识别文字

完整示例

参考 pages/vad/vad.uvue — 支持:

  • 加载 VAD 模型
  • VAD 文件检测:检测音频中的人声时间段
  • VAD 实时流:麦克风实时 VAD 事件
  • VAD + ASR 联动:VAD 检测到语音段后自动调用离线 ASR 识别
  • 取消实时任务

5. 语音合成 (TTS)

// 加载 TTS 模型
await loadModel({
  type: "tts",
  source: "static",
  modelDir: "static/assets/models/vits-melo-tts-zh_en",
  modelId: "tts-main",
  numThreads: 1,
  provider: "cpu",
});

// 查看可用角色 / 音色
const voices = await listTtsVoices("tts-main");
console.log(JSON.stringify(voices));
// 输出示例:[{ id: 'default', name: 'Default', gender: 'female' }, ...]

// 离线合成
const result = await synthesize({
  modelId: "tts-main",
  text: "你好,欢迎使用语音合成。",
  sid: 0, // speaker ID(可选)
  speed: 1.0, // 语速倍率(可选)
});
// result.path: 合成音频文件路径
// 可使用 uni.createInnerAudioContext() 播放

// 流式合成
const taskId = await synthesizeStream({
  modelId: "tts-main",
  text: "这是一段较长的文本,使用流式合成可以边生成边播放。",
  sid: 0,
  speed: 1.0,
});

完整示例

参考 pages/tts/tts.uvue — 提供:

  • 加载 TTS 模型
  • 查看 voices:列出模型支持的角色/音色列表
  • 离线合成播放:合成后播放音频文件
  • 流式合成播放:边合成边返回事件,完成后播放完整 wav
  • 重播 & 停止:重播最近合成结果,或停止当前播放
  • 取消实时任务

6. 关键词唤醒 (KWS)

// 加载 KWS 模型
await loadModel({
  type: "kws",
  source: "static",
  modelDir: "static/assets/models/kws-zipformer-zh-en",
  modelId: "kws-main",
  numThreads: 2,
  provider: "cpu",
  config: {
    modelType: "zipformer",
    encoder: "encoder-epoch-99.onnx",
    decoder: "decoder-epoch-99.onnx",
    joiner: "joiner-epoch-99.onnx",
    tokens: "tokens.txt",
    keywords: "keywords.txt", // 关键词列表文件
  },
});

// 启动麦克风唤醒监听
const taskId = await startKws({ modelId: "kws-main" });

// 取消监听
await cancelTask(taskId);

完整示例

参考 pages/kws/kws.uvue — 提供:

  • 加载 KWS 模型
  • 启动唤醒监听:麦克风持续监听,检测到关键词触发事件
  • 取消监听

7. 说话人能力 (Speaker)

// 加载说话人嵌入模型
await loadModel({
  type: "speaker-embedding",
  source: "static",
  modelDir: "static/assets/models/3dspeaker-eres2net",
  modelId: "speaker-embed",
  numThreads: 2,
  provider: "cpu",
});

// 注册声纹
await enrollSpeaker({
  modelId: "speaker-embed",
  name: "张三",
  audioPath: "/path/to/zhangsan.wav",
});

// 识别说话人
const result = await identifySpeaker({
  modelId: "speaker-embed",
  audioPath: "/path/to/test.wav",
});
console.log(result.name, result.score);

// 验证说话人
const verified = await verifySpeaker({
  modelId: "speaker-embed",
  name: "张三",
  audioPath: "/path/to/test.wav",
  threshold: 0.7,
});
console.log(verified.match, verified.score);

// 多人分段标注(需同时加载 speaker-segmentation 模型)
const diarization = await speakerDiarization({
  segmentationModelId: "seg-model",
  embeddingModelId: "speaker-embed",
  audioPath: "/path/to/multi-speaker.wav",
});
// diarization: Array<{ speaker: string, start: number, end: number }>

完整示例

参考 pages/speaker/speaker.uvue — 提供:

  • 加载 Speaker Embedding 模型
  • 加载 Speaker Segmentation 模型
  • 注册(Enroll):录入说话人声纹
  • 识别(Identify):从已注册的声纹库中匹配说话人
  • 验证(Verify):验证音频是否属于指定说话人
  • 分段(Diarize):多说话人音频分段标注

8. 文本与音频后处理

// 标点恢复
const punctuated = await punctuate({
  modelId: "punct-model",
  text: "今天天气真不错我们出去玩吧",
});
console.log(punctuated.text); // '今天天气真不错,我们出去玩吧。'

// 语种识别
const lang = await detectLanguage({
  modelId: "lang-model",
  audioPath: "/path/to/audio.wav",
});
console.log(lang.language); // 'zh' | 'en' | 'ja' | ...

// 音频标签
const tags = await audioTagging({
  modelId: "tag-model",
  audioPath: "/path/to/audio.wav",
});
console.log(JSON.stringify(tags));
// [{ label: 'Speech', score: 0.95 }, ...]

// 语音增强
const enhanced = await enhanceAudio({
  modelId: "enhance-model",
  audioPath: "/path/to/noisy.wav",
});
// enhanced.path: 增强后的音频文件路径

// 声源分离
const separated = await sourceSeparate({
  modelId: "separate-model",
  audioPath: "/path/to/mixed.wav",
});
// separated.vocals: 人声音频路径
// separated.accompaniment: 伴奏音频路径

完整示例

参考 pages/post/post.uvue — 集成了所有后处理功能的入口:

  • 标点恢复:输入无标点文本,返回带标点文本
  • 语种识别:识别音频所属语种
  • 音频标签:识别音频事件标签
  • 语音增强:去除背景噪声
  • 语音增强(流式):麦克风实时降噪
  • 声源分离:分离人声和伴奏

事件通道机制

实时功能(Streaming ASR、VAD Stream、TTS Stream、KWS 等)通过 UniApp X 事件通道返回中间结果。

// 获取当前事件通道名称
const channel = getStreamEventChannel();

// 订阅事件
uni.$on(channel, (event: string) => {
  const data = JSON.parse(event);
  switch (data.type) {
    case "asr-online-partial":
      // 实时中间结果
      console.log("partial:", data.data.text);
      break;
    case "asr-online-final":
      // 最终识别结果
      console.log("final:", data.data.text);
      break;
    case "vad-speech-segment":
      // VAD 检测到语音段
      console.log("segment:", data.data.start, data.data.end);
      break;
    case "tts-stream-chunk":
      // TTS 流式片段
      console.log("tts chunk progress:", data.data.progress);
      break;
    case "kws-keyword-detected":
      // 关键词唤醒
      console.log("keyword:", data.data.keyword);
      break;
    case "task-error":
      console.error("task error:", data.data);
      break;
  }
});

// 取消订阅(释放资源时)
uni.$off(channel);

角色、男女声与方言

  • TTS 角色/男女声/音色:由 TTS 模型自身决定,不同模型支持的 speaker 数量不同。加载后通过 listTtsVoices(modelId) 查询可用角色。
  • ASR 语种和方言:由 ASR 模型决定。SenseVoice 支持普通话、粤语、英语、日语、韩语。多语识别时传 language: 'auto' 自动检测。
  • 功能扩展性:插件不限制模型的具体能力,只要模型文件符合 sherpa-onnx 格式即可加载。通过 getModelCapabilities() 获取模型的能力清单。

常见问题

Q: 模型文件需要放在哪里?

模型可放在 uni_sherpa/static/ 目录下(跟随 App 资源打包),或下载到设备沙盒后使用绝对路径加载。

Q: iOS 和 Android 的路径差异?

  • Android:static/ 路径自动映射到 assets 资源目录,绝对路径指向 /storage/emulated/0/... 等设备存储路径。
  • iOS:static/ 路径自动映射到 App 资源 bundle。绝对路径需手动填写沙盒路径,例如 NSHomeDirectory()/Documents/models/...。iOS 推理加速可选 CoreML provider。

Q: 音频格式要求?

推荐 16kHz 采样率、单声道、16-bit 线性 PCM 的 wav 文件。其他格式可能不被支持。

Q: 如何同时加载多个模型?

多次调用 loadModel() 并传入不同的 modelId。例如同时加载 VAD 和 ASR 模型用于 VAD+ASR 联动,或同时加载 Embedding 和 Segmentation 模型用于说话人分段。

Q: 实时任务如何清理?

调用 cancelTask(taskId) 取消指定任务,或调用 releaseAll() 释放所有模型和任务。释放后应调用 uni.$off(channel) 取消事件订阅。


更多细节和完整代码示例请参考 uni_sherpa/pages/ 下的各功能页面源码。

隐私、权限声明

1. 本插件需要申请的系统权限列表:

麦克风权限(用于实时语音识别、VAD、KWS 等需要麦克风输入的功能) 存储读取权限(用于加载设备本地模型文件和读取音频文件)

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据,所有语音数据处理均在设备本地完成,不向任何服务器发送数据。

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

暂无用户评论。