更新记录

1.0.9(2026-06-11)

  • 修复安卓端在uniapp项目上云打包报错的问题

1.0.8(2026-06-06)

  • feat-重要更新:新增matchaIcefall模型,主打快速起播,过程流畅,温柔女生音色

1.0.7(2026-05-27)

  • 修复安卓端云打包错误
查看更多

平台兼容性

uni-app(5.07)

Vue2 Vue2插件版本 Vue3 Vue3插件版本 Chrome Safari app-vue app-nvue Android Android插件版本 iOS iOS插件版本 鸿蒙 鸿蒙插件版本
1.0.0 1.0.0 - - - - 5.0 1.0.0 13 1.0.5 19 1.0.1
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
- - - - - - - - - - - -

uni-app x(5.07)

Chrome Safari Android Android插件版本 iOS iOS插件版本 鸿蒙 鸿蒙插件版本 微信小程序
- - 5.0 1.0.0 13 1.0.5 19 1.0.1 -

xwq-sherpa-onnx-tts

xwq-sherpa-onnx-tts 是一个基于 sherpa-onnx 的离线 TTS 播报插件。

推荐优先使用 matchaIcefall 模型。当前项目实测它的播报速度非常快,首包响应更积极,适合作为默认首选模型。

matchaIcefall 模型初始化需要下载两个模型文件搭配使用:

第一个模型下载:
https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/matcha-icefall-zh-en.tar.bz2

解压后目录如下:
-rw-r--r--@   1 fangjun  staff    58K  4 Dec 14:29 date-zh.fst
drwxr-xr-x@ 122 fangjun  staff   3.8K 28 Nov  2023 espeak-ng-data
-rw-r--r--@   1 fangjun  staff   1.3M  4 Dec 14:29 lexicon.txt
-rw-r--r--@   1 fangjun  staff    72M  4 Dec 14:29 model-steps-3.onnx
-rw-r--r--@   1 fangjun  staff    63K  4 Dec 14:29 number-zh.fst
-rw-r--r--@   1 fangjun  staff    87K  4 Dec 14:29 phone-zh.fst
-rw-r--r--@   1 fangjun  staff   2.0K  4 Dec 14:29 README.md
-rw-r--r--@   1 fangjun  staff    21K  4 Dec 14:29 tokens.txt

第二个模型下载:
https://github.com/k2-fsa/sherpa-onnx/releases/download/vocoder-models/vocos-16khz-univ.onnx

解压后目录如下:
-rw-r--r--@ 1 fangjun  staff    51M  4 Dec 14:54 vocos-16khz-univ.onnx

插件能力概览

  • 支持基于本地 onnx 模型的离线文本转语音播报
  • 支持初始化模型、开始播报、暂停、继续、停止
  • 支持自定义 sid 说话人和 speed 语速
  • 支持传入 number.fstphone.fstdate.fst 文本归一化规则文件
  • finished 回调在当前实现中会尽量等待 AudioTrack 实际播放完成后再触发

对外暴露的方法

插件推荐通过下面方式引入:

import {
  initSherpaOnxxTts,
  start,
  pause,
  resume,
  stop
} from "@/uni_modules/xwq-sherpa-onnx-tts"

initSherpaOnxxTts(opt)

初始化 TTS 引擎和模型资源。

  • 入参:initTTsOptions
  • 返回:无
  • 说明:建议页面加载后先调用一次,初始化成功后再调用 start

start(opt)

开始播报文本内容。

  • 入参:PlayParams
  • 返回:无
  • 说明:当前实现会把文本送入离线模型生成语音,并自动写入音频播放器

pause()

暂停当前播报。

  • 入参:无
  • 返回:无

resume()

继续当前播报。

  • 入参:无
  • 返回:无

stop()

停止当前播报。

  • 入参:无
  • 返回:无

回调与类型说明

FailCallBack

type FailCallBack = {
  msg: string
  code: number
}

字段说明:

  • msg:错误信息
  • code:错误码,便于业务侧区分初始化失败、播放失败等场景

PlayParams

type PlayParams = {
  text: string
  fail?: ((val: FailCallBack) => void) | null
  finished?: (() => void) | null
}

字段说明:

  • text:需要播报的文本内容
  • fail:播报失败时触发
  • finished:当前播报完成时触发

initTTsOptions 参数表

字段名 类型 必填 说明
model string onnx 模型文件路径,例如 model.onnx
voices string | null 音色或附加模型文件路径。vitsMelo 下可传 nullmatchaIcefall 下这里需要传 vocoder 路径,例如 vocos-16khz-univ.onnx
tokens string 词表文件路径,例如 tokens.txt
dataDir string | null 发音引擎数据目录。当前 Android 的 VitsMelo 实现未使用该字段,保留接口兼容
numberFst string | null 数字文本归一化规则文件路径,例如 number.fst
phoneFst string | null 电话号码文本归一化规则文件路径,例如 phone.fst
dateFst string | null 日期文本归一化规则文件路径,例如 date.fst
dictDir string | null 分词字典目录。当前 Android 的 VitsMelo 实现未使用该字段,保留接口兼容
lexicon string 词典文件路径,用于修正特定词汇的发音
content string | null 预留字段,当前初始化流程未使用。实际播报文本请通过 start({ text }) 传入
sid number | null 说话人 ID。未传时默认使用 0
speed number | null 语速。未传时默认使用 1.0
fail ((val: FailCallBack) => void) | null 初始化失败回调
success (() => void) | null 初始化成功回调

对应类型定义如下:

type initTTsOptions = {
  model: string
  voices?: string | null
  tokens: string
  dataDir?: string | null
  numberFst?: string | null
  phoneFst?: string | null
  dateFst?: string | null
  dictDir?: string | null
  lexicon: string
  content?: string | null
  sid?: number | null
  speed?: number | null
  fail?: ((val: FailCallBack) => void) | null
  success?: (() => void) | null
}

模型参数对照表

vitsMelo

字段 是否必填 说明 当前示例值
modelType 固定传 vitsMelo vitsMelo
model 声学模型文件 model.onnx
voices 当前模型未使用,可传 null null
tokens 词表文件 tokens.txt
lexicon 发音词典 lexicon.txt
numberFst 数字归一化规则 number.fst
phoneFst 电话归一化规则 phone.fst
dateFst 日期归一化规则 date.fst
dataDir 当前 Android 示例未使用,可传 null 或空字符串 null
dictDir 当前 Android 示例未使用,可传 null 或空字符串 null
sid 说话人 ID 0
speed 语速 1.0

建议目录结构:

static/
  vits-melo-tts-zh_en/
    model.onnx
    tokens.txt
    lexicon.txt
    number.fst
    phone.fst
    date.fst

matchaIcefall

字段 是否必填 说明 当前示例值
modelType 固定传 matchaIcefall matchaIcefall
model 声学模型文件 model-steps-3.onnx
voices 这里不是 voices.bin,而是 vocoder 模型文件 vocos-16khz-univ.onnx
tokens 词表文件 tokens.txt
lexicon 发音词典 lexicon.txt
numberFst 数字归一化规则 number-zh.fst
phoneFst 电话归一化规则 phone-zh.fst
dateFst 日期归一化规则 date-zh.fst
dataDir 建议传 espeak-ng-data 目录,用于中英混合文本发音支持 espeak-ng-data/
dictDir 当前示例未使用,可传空字符串 ""
sid 当前模型一般传 0 0
speed 语速 1.0

建议目录结构:

static/
  matcha-icefall-zh-en/
    model-steps-3.onnx
    vocos-16khz-univ.onnx
    tokens.txt
    lexicon.txt
    number-zh.fst
    phone-zh.fst
    date-zh.fst
    espeak-ng-data/

补充说明:

  • matchaIcefall 至少需要两份 onnxmodelvoices(vocoder)
  • iOS 侧接入 matchaIcefall 时,voices 传入的 vocoder 文件名应和真实输出采样率保持一致,例如 vocos-16khz-univ.onnx 对应 16kHz
  • 如果 vocoder 资源和真实采样率不匹配,iOS 播放时可能出现“像快放、音调变高”的现象;排查时优先核对 modelvocoder 是否来自同一套模型资源
  • 当前插件会优先根据 vocoder 文件名中的 16khz24khz22050 等关键字推断 iOS 播放采样率,重命名模型文件时请保留这类采样率信息
  • vitsMelo 当前只需要一份主模型 model.onnx
  • 如果业务层想做模型切换,最少要同步切换 modelTypemodelvoices 和对应规则文件路径

接入步骤

1. 准备模型资源

建议把模型目录放到项目 static 下。默认推荐先接入 matchaIcefall,例如:

static/
  matcha-icefall-zh-en/
    model-steps-3.onnx
    vocos-16khz-univ.onnx
    tokens.txt
    lexicon.txt
    number-zh.fst
    phone-zh.fst
    date-zh.fst
    espeak-ng-data/

如果你想保留另一套中英双语模型做对比,也可以继续接入 vits-melo-tts-zh_en

2. 页面中初始化插件

先解析静态资源目录,再调用 initSherpaOnxxTts

  • vitsMelomodelTypevitsMelovoices 可传 null
  • matchaIcefallmodelTypematchaIcefallmodel 传声学模型,voicesvocoder 模型
  • Android / iOS 端通常需要先通过 plus.io.convertLocalFileSystemURL('/static/...') 把静态资源目录转换成应用内可访问的绝对路径,再把该路径拼接给 modeltokenslexicon 等字段
  • 鸿蒙端模型资源通常放在插件 resources/rawfile 目录下,因此页面层直接传相对目录即可,例如 vits-melo-tts-zh_en/model.onnxmatcha-icefall-zh-en/model-steps-3.onnx
  • 当前鸿蒙实现已经验证可直接把这些 rawfile 相对路径传给 sherpa-onnx,不再额外复制到应用沙箱
  • 如果页面层要做跨平台模型切换,建议把“资源根目录解析”单独封装:Android / iOS 返回本地绝对路径,鸿蒙返回 rawfile 相对目录

3. 业务触发播报

通过 start({ text }) 播放文本;如果有播放控制需求,可接入 pauseresumestop

模型下载地址

vits模型(支持中英文):https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/vits-melo-tts-zh_en.tar.bz2

推荐模型说明

  • 强烈推荐优先使用 matchaIcefall 模型,尤其适合对首包速度、整体播报速度和交互响应时间有要求的业务场景
  • matchaIcefall 在当前项目中的实际体验非常快,初始化后播报启动更积极,适合实时播报、消息通知、短文本连续朗读等场景
  • 如果你的业务重点是“尽快出声”,matchaIcefall 应该作为首选模型
  • vitsMelo 仍然可以继续使用,适合做兼容对比或保留另一套中英双语模型方案,但默认推荐顺序应优先考虑 matchaIcefall

TTS说明

  • 模型下载地址TTS模型下载
  • 强烈推荐使用 matchaIcefall 模型,速度非常快,适合作为默认首选模型
  • vits-melo-tts-zh_en 模型同样支持中英双语,可作为备用模型或效果对比模型
  • 暂停/恢复功能依赖于底层模型实现,部分模型可能不支持真正的暂停

uni-app x 页面用例

vitsMelo 页面调用示例

<template>
  <view class="page">
    <button @click="initTTs">初始化TTS</button>
    <button @click="playTts">开始播报</button>
    <button @click="pauseTts">暂停</button>
    <button @click="resumeTts">继续</button>
    <button @click="stopTts">停止</button>
  </view>
</template>

<script setup>
import {
  initSherpaOnxxTts,
  start,
  pause,
  resume,
  stop
} from "@/uni_modules/xwq-sherpa-onnx-tts"

const content = ref("清晨推开窗,一股清冽的寒气扑面而来")

const initTTs = () => {
  const path = "static/vits-melo-tts-zh_en"
  const staticPath = UTSAndroid.getResourcePath(path)
  if (staticPath == null || staticPath == "") {
    console.log("VITS 静态资源路径解析失败")
    return
  }

  initSherpaOnxxTts({
    modelType: "vitsMelo",
    model: staticPath + "/model.onnx",
    voices: null,
    tokens: staticPath + "/tokens.txt",
    dataDir: null,
    lexicon: staticPath + "/lexicon.txt",
    numberFst: staticPath + "/number.fst",
    phoneFst: staticPath + "/phone.fst",
    dateFst: staticPath + "/date.fst",
    sid: 0,
    speed: 1.0,
    success: () => {
      console.log("初始化成功")
    },
    fail: (result) => {
      console.log("初始化失败", result)
    }
  })
}

const playTts = () => {
  start({
    text: content.value,
    finished: () => {
      console.log("播放完成")
    },
    fail: (result) => {
      console.log("播放失败", result)
    }
  })
}

const pauseTts = () => {
  pause()
}

const resumeTts = () => {
  resume()
}

const stopTts = () => {
  stop()
}
</script>

<style>
.page {
  display: flex;
  flex-direction: column;
  padding: 24px;
  gap: 12px;
}
</style>

matchaIcefall 页面调用示例

<template>
  <view class="page">
    <button @click="initTTs">初始化TTS</button>
    <button @click="playTts">开始播报</button>
    <button @click="pauseTts">暂停</button>
    <button @click="resumeTts">继续</button>
    <button @click="stopTts">停止</button>
  </view>
</template>

<script setup>
import { ref } from "vue"
import {
  initSherpaOnxxTts,
  start,
  pause,
  resume,
  stop
} from "@/uni_modules/xwq-sherpa-onnx-tts"

const content = ref("中英文合成测试。Today is a bright day, welcome to try the matcha icefall zh en model.")

const initTTs = () => {
  const path = "static/matcha-icefall-zh-en"
  const staticPath = UTSAndroid.getResourcePath(path)
  if (staticPath == null || staticPath == "") {
    console.log("matcha 静态资源路径解析失败")
    return
  }

  initSherpaOnxxTts({
    modelType: "matchaIcefall",
    model: staticPath + "/model-steps-3.onnx",
    voices: staticPath + "/vocos-16khz-univ.onnx",
    tokens: staticPath + "/tokens.txt",
    dataDir: staticPath + "/espeak-ng-data",
    dictDir: "",
    lexicon: staticPath + "/lexicon.txt",
    numberFst: staticPath + "/number-zh.fst",
    phoneFst: staticPath + "/phone-zh.fst",
    dateFst: staticPath + "/date-zh.fst",
    sid: 0,
    speed: 1.0,
    success: () => {
      console.log("matcha 初始化成功")
    },
    fail: (result) => {
      console.log("matcha 初始化失败", result)
    }
  })
}

const playTts = () => {
  start({
    text: content.value,
    finished: () => {
      console.log("播放完成")
    },
    fail: (result) => {
      console.log("播放失败", result)
    }
  })
}

const pauseTts = () => {
  pause()
}

const resumeTts = () => {
  resume()
}

const stopTts = () => {
  stop()
}
</script>

<style>
.page {
  display: flex;
  flex-direction: column;
  padding: 24px;
  gap: 12px;
}
</style>

uni-app 页面用例

如果是传统 uni-app 页面,静态资源路径通常可以通过 plus.io.convertLocalFileSystemURL 获取。

vitsMelo 页面调用示例

<template>
  <view class="page">
    <button @click="initTTs">初始化TTS</button>
    <button @click="playTts">开始播报</button>
    <button @click="pauseTts">暂停</button>
    <button @click="resumeTts">继续</button>
    <button @click="stopTts">停止</button>
  </view>
</template>

<script>
import {
  initSherpaOnxxTts,
  start,
  pause,
  resume,
  stop
} from "@/uni_modules/xwq-sherpa-onnx-tts"

export default {
  data() {
    return {
      content: "清晨推开窗,一股清冽的寒气扑面而来"
    }
  },
  methods: {
    initTTs() {
      const path = "/static/vits-melo-tts-zh_en"
      const staticPath = plus.io.convertLocalFileSystemURL(path)
      if (!staticPath) {
        console.log("VITS 静态资源路径解析失败")
        return
      }

      initSherpaOnxxTts({
        modelType: "vitsMelo",
        model: staticPath + "/model.onnx",
        voices: null,
        tokens: staticPath + "/tokens.txt",
        dataDir: null,
        lexicon: staticPath + "/lexicon.txt",
        numberFst: staticPath + "/number.fst",
        phoneFst: staticPath + "/phone.fst",
        dateFst: staticPath + "/date.fst",
        sid: 0,
        speed: 1.0,
        success: () => {
          console.log("初始化成功")
        },
        fail: (result) => {
          console.log("初始化失败", result)
        }
      })
    },
    playTts() {
      start({
        text: this.content,
        finished: () => {
          console.log("播放完成")
        },
        fail: (result) => {
          console.log("播放失败", result)
        }
      })
    },
    pauseTts() {
      pause()
    },
    resumeTts() {
      resume()
    },
    stopTts() {
      stop()
    }
  }
}
</script>

<style>
.page {
  padding: 24rpx;
}
</style>

matchaIcefall 页面调用示例

<template>
  <view class="page">
    <button @click="initTTs">初始化TTS</button>
    <button @click="playTts">开始播报</button>
    <button @click="pauseTts">暂停</button>
    <button @click="resumeTts">继续</button>
    <button @click="stopTts">停止</button>
  </view>
</template>

<script>
import {
  initSherpaOnxxTts,
  start,
  pause,
  resume,
  stop
} from "@/uni_modules/xwq-sherpa-onnx-tts"

export default {
  data() {
    return {
      content: "中英文合成测试。Today is a bright day, welcome to try the matcha icefall zh en model."
    }
  },
  methods: {
    initTTs() {
      const path = "/static/matcha-icefall-zh-en"
      const staticPath = plus.io.convertLocalFileSystemURL(path)
      if (!staticPath) {
        console.log("matcha 静态资源路径解析失败")
        return
      }

      initSherpaOnxxTts({
        modelType: "matchaIcefall",
        model: staticPath + "/model-steps-3.onnx",
        voices: staticPath + "/vocos-16khz-univ.onnx",
        tokens: staticPath + "/tokens.txt",
        dataDir: staticPath + "/espeak-ng-data",
        dictDir: "",
        lexicon: staticPath + "/lexicon.txt",
        numberFst: staticPath + "/number-zh.fst",
        phoneFst: staticPath + "/phone-zh.fst",
        dateFst: staticPath + "/date-zh.fst",
        sid: 0,
        speed: 1.0,
        success: () => {
          console.log("matcha 初始化成功")
        },
        fail: (result) => {
          console.log("matcha 初始化失败", result)
        }
      })
    },
    playTts() {
      start({
        text: this.content,
        finished: () => {
          console.log("播放完成")
        },
        fail: (result) => {
          console.log("播放失败", result)
        }
      })
    },
    pauseTts() {
      pause()
    },
    resumeTts() {
      resume()
    },
    stopTts() {
      stop()
    }
  }
}
</script>

<style>
.page {
  padding: 24rpx;
}
</style>

鸿蒙端 页面用例

使用前注意

 * 鸿蒙端使用sherpa_onnx语音识别特别说明: 
 * 将路径:unpackage\dist\dev\app-harmony\entry\build-profile.json5文件拷贝到根目录harmony-configs/entry下(目录没有的需要手动创建)
 * 
 * 在harmony-configs/entry/build-profile.json5中配置参数字段:buildOption下面新增以下内容
 * "sourceOption": {
 *     "workers": [
 *      '../uni_modules/xwq-sherpa-onnx/utssdk/app-harmony/TtsWorker.ets'
 *    ]
 *  }

 模型资源需要放在插件目录resources/rawfile目录下面
  • 鸿蒙端当前也已支持和 Android / iOS 一致的 modelType 分流策略
  • vitsMelomodelTypevitsMelovoices 可传 null
  • matchaIcefallmodelTypematchaIcefallmodel 传声学模型,voicesvocoder 模型,例如 vocos-16khz-univ.onnx
  • 如果要在鸿蒙端做模型切换,至少要同步切换 modelTypemodelvoicestokenslexicon 以及对应的 fst / dataDir 路径
  • 当前鸿蒙端资源解析链路为“页面层传 rawfile 相对路径 -> worker 直接把路径传给 sherpa-onnx”,不再执行额外的资源复制

vitsMelo 页面调用示例

<template>
  <view class="page">
    <button @click="initTTs">初始化TTS</button>
    <button @click="playTts">开始播报</button>
    <button @click="pauseTts">暂停</button>
    <button @click="resumeTts">继续</button>
    <button @click="stopTts">停止</button>
  </view>
</template>

<script>
import {
  initSherpaOnxxTts,
  start,
  pause,
  resume,
  stop
} from "@/uni_modules/xwq-sherpa-onnx-tts"

export default {
  data() {
    return {
      content: "清晨推开窗,一股清冽的寒气扑面而来"
    }
  },
  methods: {
    initTTs() {
      const path = "vits-melo-tts-zh_en"

      initSherpaOnxxTts({
            modelType:"vitsMelo",
            model:path+"/model.onnx",
            voices:null,
            dataDir:"",
            dictDir:"",
            tokens:path+"/tokens.txt",
            lexicon:`${path}/lexicon.txt`,
            numberFst:`${path}/number.fst`,
            phoneFst:`${path}/phone.fst`,
            dateFst:`${path}/date.fst`,
            // sid:0,
            // speed:1.0,
            success: () => {
                console.log('初始化成功')
                // onnxStart()
            },
            fail: (result) => {
                console.log('初始化失败',result)
           }
       })
    },
    playTts() {
      start({
        text: this.content,
        finished: () => {
          console.log("播放完成")
        },
        fail: (result) => {
          console.log("播放失败", result)
        }
      })
    },
    pauseTts() {
      pause()
    },
    resumeTts() {
      resume()
    },
    stopTts() {
      stop()
    }
  }
}
</script>

<style>
.page {
  padding: 24rpx;
}
</style>

matchaIcefall 页面调用示例

<template>
  <view class="page">
    <button @click="initTTs">初始化TTS</button>
    <button @click="playTts">开始播报</button>
    <button @click="pauseTts">暂停</button>
    <button @click="resumeTts">继续</button>
    <button @click="stopTts">停止</button>
  </view>
</template>

<script>
import {
  initSherpaOnxxTts,
  start,
  pause,
  resume,
  stop
} from "@/uni_modules/xwq-sherpa-onnx-tts"

export default {
  data() {
    return {
      content: "中英文合成测试。Today is a bright day, welcome to try the matcha icefall zh en model."
    }
  },
  methods: {
    initTTs() {
      const path = "matcha-icefall-zh-en"

      initSherpaOnxxTts({
            modelType:"matchaIcefall",
            model:path+"/model-steps-3.onnx",
            voices:path+"/vocos-16khz-univ.onnx",
            tokens:path+"/tokens.txt",
            dataDir:path+"/espeak-ng-data",
            dictDir:"",
            lexicon:`${path}/lexicon.txt`,
            numberFst:`${path}/number-zh.fst`,
            phoneFst:`${path}/phone-zh.fst`,
            dateFst:`${path}/date-zh.fst`,
            success: () => {
                console.log('matcha 初始化成功')
            },
            fail: (result) => {
                console.log('matcha 初始化失败',result)
           }
       })
    },
    playTts() {
      start({
        text: this.content,
        finished: () => {
          console.log("播放完成")
        },
        fail: (result) => {
          console.log("播放失败", result)
        }
      })
    },
    pauseTts() {
      pause()
    },
    resumeTts() {
      resume()
    },
    stopTts() {
      stop()
    }
  }
}
</script>

<style>
.page {
  padding: 24rpx;
}
</style>

使用建议

  • 建议在页面进入后先调用一次 initSherpaOnxxTts
  • 初始化完成前不要直接调用 start
  • 模型、词表、词典、规则文件建议保持在同一静态目录下,便于维护
  • 如果是短文本连续播报,建议在上一次 finished 后再发起下一次播报,避免业务层抢占播放状态
  • 如果 fail 回调收到错误,请先检查模型路径、词表路径和静态资源是否已正确打包到应用内

隐私、权限声明

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

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

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