更新记录
1.0.0(2026-06-22)
tts
平台兼容性
TTS 语音合成插件 — 使用手册
让你的 uni-app 项目拥有"开口说话"的能力。
支持平台: Android 5.0+ | iOS 12.0+ | 鸿蒙 NEXT 4.0+
目录
- 零、5 分钟快速体验
- 一、完整 Demo 页面(复制即用)
- 二、API 详细说明
- 三、参数详解
- 四、回调函数详解
- 五、实用场景示例
- 六、错误处理
- 七、平台差异
- 八、常见问题排查
- 九、Android 安装 TTS 引擎指南
- 十、API 速查
零、5 分钟快速体验
第 1 步:引入插件
在 .vue 文件的 <script setup> 中:
import { ttsSpeak } from '@/uni_modules/sehochen-tts'
第 2 步:一行代码让手机说话
ttsSpeak({ text: "你好,世界!" })
手机就会读出"你好,世界!"。
第 3 步:加上回调,知道播放状态
import { ttsSpeak, ttsStop } from '@/uni_modules/sehochen-tts'
// 播放
ttsSpeak({
text: "你好,欢迎使用TTS语音合成",
lang: "zh-CN", // 中文
rate: 1.0, // 正常语速
onStart: () => console.log("开始播放了"),
onDone: () => console.log("播放完成"),
fail: (err) => console.log("出错:", err.errMsg)
})
// 随时可以停止
ttsStop()
5 分钟,你已经掌握了核心用法。
一、完整 Demo 页面(复制即用)
以下代码复制到一个 .vue 文件中即可运行。包含:文本输入、语言切换、播放/暂停/恢复/停止、语速/音调/音量滑块、引擎状态、日志。
<template>
<view class="page">
<view class="title">TTS 语音合成</view>
<!-- 文本输入 -->
<view class="box">
<textarea v-model="text" placeholder="输入文字,点播放按钮朗读" />
</view>
<!-- 语言快捷选择 -->
<view class="lang-row">
<view v-for="item in langList" :key="item.lang"
:class="['lang-tag', { active: lang === item.lang }]"
@click="switchLang(item)">
{{ item.label }}
</view>
</view>
<!-- 播放控制 -->
<view class="btn-row">
<button class="btn-play" @click="play">播放</button>
<button class="btn-pause" @click="pause">暂停</button>
<button class="btn-resume" @click="resume">恢复</button>
<button class="btn-stop" @click="stop">停止</button>
</view>
<!-- 状态提示 -->
<view v-if="status" class="status">{{ status }}</view>
<!-- 语速滑块 -->
<view class="box">
<view class="slider-head">
<text>语速</text>
<text class="slider-val">{{ rate.toFixed(1) }}x</text>
</view>
<slider :min="0.5" :max="2.0" :step="0.1" :value="rate"
activeColor="#007aff" @change="rate = $event.detail.value" />
</view>
<!-- 音调滑块 -->
<view class="box">
<view class="slider-head">
<text>音调</text>
<text class="slider-val">{{ pitch.toFixed(1) }}</text>
</view>
<slider :min="0.5" :max="2.0" :step="0.1" :value="pitch"
activeColor="#007aff" @change="pitch = $event.detail.value" />
</view>
<!-- 音量滑块 -->
<view class="box">
<view class="slider-head">
<text>音量</text>
<text class="slider-val">{{ (volume * 100).toFixed(0) }}%</text>
</view>
<slider :min="0.0" :max="1.0" :step="0.1" :value="volume"
activeColor="#007aff" @change="volume = $event.detail.value" />
</view>
<!-- 引擎状态 -->
<view class="box">
<view class="section-title">引擎状态</view>
<view class="info-row"><text>引擎名称:</text><text>{{ engine.engineName }}</text></view>
<view class="info-row"><text>默认语言:</text><text>{{ engine.defaultLanguage }}</text></view>
<view class="info-row"><text>默认人声:</text><text>{{ engine.defaultVoice }}</text></view>
<view class="info-row"><text>初始化状态:</text><text>{{ statusText(engine.initStatus) }}</text></view>
<view class="info-row"><text>是否播放中:</text><text>{{ engine.isSpeaking ? '是' : '否' }}</text></view>
<view class="info-row"><text>语言可用:</text><text>{{ engine.isLanguageAvailable ? '是' : '否' }}</text></view>
<view class="refresh-btn" @click="refreshEngine">刷新状态</view>
</view>
<!-- 日志 -->
<view class="box">
<view class="section-title">
<text>日志</text>
<text class="clear-btn" @click="logs = []">清空</text>
</view>
<view v-if="logs.length === 0" class="empty-text">暂无日志</view>
<view v-for="(item, i) in logs" :key="i" :class="['log-item', item.type]">
<text class="log-time">{{ item.time }}</text>
<text>{{ item.msg }}</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import {
ttsSpeak, ttsStop, ttsPause, ttsResume,
ttsGetEngineInfo, ttsGetInstalledEngines
} from '@/uni_modules/sehochen-tts'
// 播放参数
const text = ref('你好,世界!欢迎使用TTS语音合成。')
const rate = ref(1.0)
const pitch = ref(1.0)
const volume = ref(1.0)
const lang = ref('zh-CN')
const status = ref('')
// 引擎状态
const engine = ref({
engineName: '加载中...',
defaultLanguage: '-',
defaultVoice: '-',
initStatus: 0,
isSpeaking: false,
isLanguageAvailable: false
})
// 日志
const logs = ref([])
function addLog(msg, type = 'info') {
const d = new Date()
const pad = (n) => String(n).padStart(2, '0')
const time = `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
logs.value.unshift({ time, msg, type })
if (logs.value.length > 30) logs.value.pop()
}
function statusText(s) {
const map = { 0: '未初始化', 1: '初始化中', 2: '已就绪', '-1': '失败' }
return map[s] || '未知'
}
function refreshEngine() {
try {
engine.value = ttsGetEngineInfo()
addLog('引擎状态已刷新', 'info')
} catch (e) {
addLog('刷新失败: ' + e, 'error')
}
}
// 语言列表
const langList = [
{ label: '中文', lang: 'zh-CN', text: '你好,世界!欢迎使用TTS语音合成。' },
{ label: 'English', lang: 'en-US', text: 'Hello, welcome to TTS speech synthesis.' },
{ label: '日本語', lang: 'ja-JP', text: 'こんにちは、TTS音声合成へようこそ。' },
{ label: '한국어', lang: 'ko-KR', text: '안녕하세요, TTS 음성 합성에 오신 것을 환영합니다.' },
{ label: 'Français', lang: 'fr-FR', text: 'Bonjour, bienvenue dans la synthèse vocale TTS.' },
{ label: 'Русский', lang: 'ru-RU', text: 'Здравствуйте, добро пожаловать в синтез речи TTS.' }
]
function switchLang(item) {
text.value = item.text
lang.value = item.lang
addLog(`切换到 ${item.label}`, 'info')
}
// 播放控制
function play() {
if (!text.value.trim()) {
uni.showToast({ title: '请先输入文字', icon: 'none' })
return
}
status.value = '正在播放...'
addLog(`播放 [${lang.value}]: ${text.value.substring(0, 20)}...`, 'info')
ttsSpeak({
text: text.value,
lang: lang.value,
rate: rate.value,
pitch: pitch.value,
volume: volume.value,
onStart: () => {
addLog('开始合成', 'success')
},
onDone: () => {
status.value = '播放完成'
addLog('播放完成', 'success')
},
onStop: () => {
status.value = '已停止'
addLog('播放被中断', 'warn')
},
fail: (err) => {
status.value = ''
addLog(`播放失败 [${err.errCode}]: ${err.errMsg}`, 'error')
uni.showToast({ title: err.errMsg, icon: 'none', duration: 3000 })
}
})
}
function pause() {
ttsPause()
status.value = '已暂停'
addLog('暂停', 'warn')
}
function resume() {
ttsResume()
status.value = '正在播放...'
addLog('恢复播放', 'info')
}
function stop() {
ttsStop()
status.value = '已停止'
addLog('手动停止', 'warn')
}
onMounted(() => {
addLog('页面就绪', 'info')
setTimeout(refreshEngine, 500)
})
</script>
<style>
.page { padding: 20px; background: #f5f5f5; min-height: 100vh; }
.title { font-size: 24px; font-weight: bold; text-align: center; margin-bottom: 20px; }
.box { background: #fff; padding: 15px; border-radius: 10px; margin-bottom: 12px; }
textarea { width: 100%; min-height: 60px; font-size: 16px; }
.lang-row { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 12px; }
.lang-tag { padding: 8px 16px; border-radius: 20px; background: #eee; font-size: 14px; }
.lang-tag.active { background: #007aff; color: #fff; }
.btn-row { display: flex; gap: 8px; margin-bottom: 12px; }
.btn-row button { flex: 1; height: 42px; font-size: 15px; border-radius: 8px; color: #fff; border: none; }
.btn-play { background: #007aff; }
.btn-pause { background: #ff9500; }
.btn-resume { background: #34c759; }
.btn-stop { background: #ff3b30; }
.status { text-align: center; color: #007aff; margin-bottom: 12px; font-size: 14px; }
.slider-head { display: flex; justify-content: space-between; margin-bottom: 5px; font-size: 15px; }
.slider-val { color: #007aff; font-weight: bold; }
.section-title { font-size: 16px; font-weight: bold; margin-bottom: 10px; display: flex; justify-content: space-between; }
.info-row { display: flex; padding: 4px 0; font-size: 14px; }
.info-row text:first-child { color: #999; width: 100px; }
.info-row text:last-child { color: #333; }
.refresh-btn { text-align: center; color: #007aff; font-size: 14px; margin-top: 10px; padding: 6px; background: #e8f0fe; border-radius: 16px; }
.clear-btn { font-size: 14px; color: #ff3b30; font-weight: normal; }
.empty-text { text-align: center; color: #ccc; font-size: 14px; padding: 20px 0; }
.log-item { display: flex; gap: 10px; padding: 6px 0; border-bottom: 1px solid #f0f0f0; font-size: 13px; }
.log-time { color: #ccc; font-family: monospace; flex-shrink: 0; }
.log-item.info { color: #666; }
.log-item.success { color: #34c759; }
.log-item.warn { color: #ff9500; }
.log-item.error { color: #ff3b30; }
</style>
二、API 详细说明
2.1 ttsSpeak — 播放文字
让手机朗读一段文字。首次调用会自动初始化 TTS 引擎(异步,约 1~3 秒)。
import { ttsSpeak } from '@/uni_modules/sehochen-tts'
ttsSpeak({
text: "你好,欢迎使用TTS", // 【必填】要朗读的文字
lang: "zh-CN", // 可选,语言代码,默认 "zh-CN"
rate: 1.0, // 可选,语速 0.5~2.0,默认 1.0
pitch: 1.0, // 可选,音调 0.5~2.0,默认 1.0
volume: 1.0, // 可选,音量 0.0~1.0,默认 1.0
voice: "", // 可选,指定人声名称
// 回调函数(全部可选)
onStart: () => {}, // 开始播放
onDone: () => {}, // 自然播放完毕
onStop: () => {}, // 被 ttsStop() 中断
success: () => {}, // 播放成功(onDone 之后)
fail: (err) => {}, // 播放失败
complete:() => {}, // 无论成功失败都触发(最后)
})
行为说明:
- 如果当前正在播放,新调用会自动停止旧播放,然后开始新播放(
QUEUE_FLUSH模式) - 如果引擎未初始化,会自动触发初始化并等待完成后播放
- 所有参数除
text外都是可选的,不传则使用默认值
2.2 ttsStop — 停止播放
立即停止当前正在播放的语音。
import { ttsStop } from '@/uni_modules/sehochen-tts'
ttsStop()
效果:
- 触发当前播放的
onStop回调 - 清除暂停状态(之后调用
ttsResume无效) - 引擎保持可用,可立即再次调用
ttsSpeak
注意:如果当前没有在播放,调用也不会报错,只是什么都不做。
2.3 ttsPause — 暂停播放
暂停当前播放的语音,之后可以用 ttsResume() 从断点处继续。
import { ttsPause } from '@/uni_modules/sehochen-tts'
ttsPause()
前提条件:必须在播放中(isSpeakingFlag === true)且不在暂停状态。
平台实现差异:
| 平台 | 实现方式 | 精度 |
|---|---|---|
| iOS | 调用原生 pauseSpeaking(at: .word) |
✅ 精确到单词 |
| Android | 记录已播放时长 → stop() → 恢复时估算断点续播 |
⚠️ 按时间估算(中文约 4 字/秒) |
| 鸿蒙 | 同 Android,记录时长后估算断点续播 | ⚠️ 按时间估算 |
注意事项:
- 暂停不会触发
onStop回调 - 暂停后如果调用了
ttsStop()或新的ttsSpeak(),暂停状态会被清除 - Android/鸿蒙的暂停是模拟实现,不是音频级暂停,恢复时位置有 1~3 个字的偏差
2.4 ttsResume — 恢复播放
从暂停处继续播放。
import { ttsResume } from '@/uni_modules/sehochen-tts'
ttsResume()
前提条件:
- 必须之前调用过
ttsPause()并且暂停状态未被清除 - 如果期间调用了
ttsStop()或ttsSpeak(),暂停状态会被清除,ttsResume()无效果
Android/鸿蒙恢复逻辑:
- 根据语速估算已播放的字符数(
已播秒数 × 每秒字符数) - 从文本中截取剩余部分
- 用剩余文本重新开始播放
- 原始的所有回调(
onDone、fail等)会被保留并绑定到新播放上
恢复后的回调流程:和正常播放一样,onStart → onDone → success → complete。
2.5 ttsGetEngineInfo — 查看引擎状态
返回当前 TTS 引擎的详细信息。
import { ttsGetEngineInfo } from '@/uni_modules/sehochen-tts'
const info = ttsGetEngineInfo()
返回值(TTSEngineInfo 对象):
| 字段 | 类型 | 说明 |
|---|---|---|
engineName |
string |
引擎名称,如 "com.google.android.tts" |
defaultLanguage |
string |
默认语言,如 "zh_CN" |
defaultVoice |
string |
默认人声名称 |
initStatus |
number |
初始化状态(见下表) |
isSpeaking |
boolean |
是否正在播放 |
isLanguageAvailable |
boolean |
默认语言是否可用 |
maxSpeechInputLength |
number |
最大输入字符数,-1 表示未知 |
installedEngines |
string[] |
已安装引擎列表 |
initStatus 含义:
| 值 | 含义 | 此时能播放吗? |
|---|---|---|
0 |
未初始化 | 调用 ttsSpeak 会自动触发初始化 |
1 |
初始化中 | 等 1~3 秒变成 2 就可以播 |
2 |
已就绪 | ✅ 可以正常播放 |
-1 |
初始化失败 | ❌ 需要安装 TTS 引擎 |
2.6 ttsGetInstalledEngines — 已安装引擎列表
返回设备上所有已安装的 TTS 引擎包名。
import { ttsGetInstalledEngines } from '@/uni_modules/sehochen-tts'
const engines = ttsGetInstalledEngines()
// 返回: ["com.google.android.tts", "com.iflytek.speechcloud"]
如果返回空数组 [],说明设备没有安装任何 TTS 引擎,需要安装。
2.7 ttsShutdown — 释放引擎
释放 TTS 引擎占用的系统资源。所有状态会重置。
import { ttsShutdown } from '@/uni_modules/sehochen-tts'
ttsShutdown()
使用时机:
- 页面退出时(
onUnload) - APP 不再需要 TTS 功能时
- 想释放内存时
释放后的影响:
initStatus变为0isSpeaking变为false- 暂停状态被清除
- 当前播放选项被清除
- 下次调用
ttsSpeak会自动重新初始化,无需手动操作
三、参数详解
3.1 text — 要朗读的文字
- 类型:
string - 必填:✅ 是
- 限制:取决于引擎,通常 Android 约 4000 字符,iOS 无明确限制
// 最短用法
ttsSpeak({ text: "你好" })
// 长文本也可以
ttsSpeak({ text: "这是一段很长的文字..." })
如果传入空字符串 "" 或不传,会触发 fail 回调(错误码 9020004)。
3.2 lang — 语言代码
- 类型:
string - 默认值:
"zh-CN" - 格式:
语言-地区(BCP-47 标准)
ttsSpeak({ text: "Hello", lang: "en-US" }) // 英文
ttsSpeak({ text: "你好", lang: "zh-CN" }) // 中文
ttsSpeak({ text: "こんにちは", lang: "ja-JP" }) // 日文
常用语言代码:
| 代码 | 语言 | 通常是否需要下载语言包 |
|---|---|---|
zh-CN |
中文(简体) | 一般自带 |
en-US |
英语(美国) | 可能需要下载 |
en-GB |
英语(英国) | 可能需要下载 |
ja-JP |
日语 | 可能需要下载 |
ko-KR |
韩语 | 可能需要下载 |
fr-FR |
法语 | 可能需要下载 |
de-DE |
德语 | 可能需要下载 |
es-ES |
西班牙语 | 可能需要下载 |
pt-BR |
葡萄牙语(巴西) | 可能需要下载 |
ru-RU |
俄语 | 可能需要下载 |
it-IT |
意大利语 | 可能需要下载 |
切换语言后没声音? 去手机设置下载对应语言包。详见 第九节。
3.3 rate — 语速
- 类型:
number - 范围:
0.5~2.0 - 默认值:
1.0
控制朗读的快慢。超出范围会触发错误 9020005。
| 值 | 效果 | 适合场景 |
|---|---|---|
| 0.5 ~ 0.7 | 很慢 | 学外语、听力练习 |
| 0.8 ~ 1.0 | 偏慢 | 老年人、儿童 |
| 1.0 ~ 1.2 | 正常 | 日常使用 |
| 1.3 ~ 1.6 | 偏快 | 快速浏览 |
| 1.7 ~ 2.0 | 很快 | 赶时间 |
ttsSpeak({ text: "我读得很慢", rate: 0.6 })
ttsSpeak({ text: "我读得很快", rate: 1.8 })
3.4 pitch — 音调
- 类型:
number - 范围:
0.5~2.0 - 默认值:
1.0
控制声音的高低(频率)。超出范围会触发错误 9020005。
| 值 | 效果 |
|---|---|
| 0.5 ~ 0.8 | 低沉(偏男声) |
| 0.9 ~ 1.2 | 正常 |
| 1.3 ~ 2.0 | 尖细(偏童声) |
ttsSpeak({ text: "低沉的声音", pitch: 0.6 })
ttsSpeak({ text: "尖细的声音", pitch: 1.8 })
3.5 volume — 音量
- 类型:
number - 范围:
0.0~1.0 - 默认值:
1.0
它是在系统媒体音量基础上的倍率,不是绝对音量。
| 值 | 效果 |
|---|---|
0.0 |
静音(听不到) |
0.5 |
系统音量的一半 |
1.0 |
等于系统当前音量(最大) |
ttsSpeak({ text: "轻声说", volume: 0.3 })
ttsSpeak({ text: "大声说", volume: 1.0 })
注意:
volume不能超过系统音量。比如系统音量只开了 50%,插件volume: 1.0也只能播放 50% 的音量。
3.6 voice — 指定人声
- 类型:
string - 默认值:
""(使用引擎默认人声)
指定使用哪个语音包(人声)。不同引擎支持的人声不同。
// 使用默认人声(不传或传空字符串)
ttsSpeak({ text: "你好" })
// 指定特定人声(需要引擎支持)
ttsSpeak({ text: "你好", voice: "zh-CN-Xiaoxiao" })
大多数场景不需要设置此参数,引擎会自动选择最匹配的人声。
四、回调函数详解
4.1 回调一览
| 回调 | 类型 | 触发时机 | 触发条件 |
|---|---|---|---|
onStart |
() => void |
引擎开始合成/播放 | 播放成功启动 |
onDone |
() => void |
整段文字自然播放完毕 | 没有被 stop 中断 |
onStop |
() => void |
被 ttsStop() 中断 |
手动停止或新播放覆盖 |
success |
() => void |
播放成功 | 跟在 onDone 之后 |
fail |
(err: TTSFail) => void |
播放出错 | 引擎错误、参数错误等 |
complete |
() => void |
播放流程结束 | 成功或失败后都会触发 |
4.2 完整生命周期图
场景 A:正常播放完成
ttsSpeak()
→ 引擎初始化(如果需要)
→ onStart() ← 开始播放
→ (播放中...)
→ onDone() ← 自然播放完毕
→ success() ← 紧随 onDone
→ complete() ← 最后触发(无论成功失败)
场景 B:手动停止
ttsSpeak()
→ onStart()
→ (播放中...)
→ 用户调用 ttsStop()
→ onStop() ← 被中断(不会触发 onDone/success/complete)
场景 C:播放出错
ttsSpeak()
→ (初始化或播放过程中出错)
→ fail(err) ← 错误信息
→ complete() ← 仍然会触发
场景 D:暂停/恢复
ttsSpeak()
→ onStart()
→ (播放中...)
→ 用户调用 ttsPause() ← 不触发任何回调
→ (一段时间...)
→ 用户调用 ttsResume()
→ onStart() ← 从断点恢复
→ (继续播放剩余文本...)
→ onDone() → success() → complete()
4.3 各场景回调触发表
| 场景 | onStart | onDone | onStop | success | fail | complete |
|---|---|---|---|---|---|---|
| 正常播放完成 | ✅ | ✅ | ❌ | ✅ | ❌ | ✅ |
| 手动 ttsStop | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
| 新播放覆盖旧播放 | ❌ | ❌ | ✅(旧) | ❌ | ❌ | ❌ |
| ttsPause | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| 暂停后恢复完成 | ✅ | ✅ | ❌ | ✅ | ❌ | ✅ |
| 播放出错 | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ |
4.4 fail 回调的 err 对象
fail: (err) => {
err.errCode // number — 错误码,如 9020003
err.errMsg // string — 人类可读的错误描述,可直接展示
err.detail // string | null — 技术详情,用于排查
}
所有错误码及含义见 第六节:错误处理。
五、实用场景示例
场景 1:播放/停止切换按钮
一个按钮,点一下播放,再点一下停止:
import { ref } from 'vue'
import { ttsSpeak, ttsStop } from '@/uni_modules/sehochen-tts'
const isPlaying = ref(false)
function toggle(text) {
if (isPlaying.value) {
ttsStop()
isPlaying.value = false
return
}
isPlaying.value = true
ttsSpeak({
text,
onDone: () => { isPlaying.value = false },
onStop: () => { isPlaying.value = false },
fail: () => { isPlaying.value = false }
})
}
场景 2:顺序朗读列表
const playlist = [
{ text: "第一段文字", lang: "zh-CN" },
{ text: "Second paragraph", lang: "en-US" },
{ text: "第三段文字", lang: "zh-CN" }
]
let index = 0
function playNext() {
if (index >= playlist.length) {
console.log("全部播放完毕")
return
}
const item = playlist[index]
ttsSpeak({
text: item.text,
lang: item.lang,
onDone: () => {
index++
setTimeout(playNext, 300) // 段与段之间间隔 0.3 秒
},
fail: (err) => {
console.error(`第 ${index + 1} 段播放失败:`, err.errMsg)
index++
playNext() // 跳过失败的继续
}
})
}
playNext()
场景 3:带暂停/恢复的播放器
import { ref } from 'vue'
import { ttsSpeak, ttsStop, ttsPause, ttsResume } from '@/uni_modules/sehochen-tts'
const isPlaying = ref(false)
const isPaused = ref(false)
// 开始播放
function play(text) {
isPlaying.value = true
isPaused.value = false
ttsSpeak({
text,
onStart: () => { isPlaying.value = true },
onDone: () => { resetState() },
onStop: () => { resetState() },
fail: () => { resetState() }
})
}
// 暂停/恢复切换
function togglePause() {
if (!isPlaying.value) return
if (isPaused.value) {
ttsResume()
isPaused.value = false
} else {
ttsPause()
isPaused.value = true
}
}
// 停止
function stop() {
ttsStop()
resetState()
}
function resetState() {
isPlaying.value = false
isPaused.value = false
}
场景 4:朗读并高亮当前句子
import { ref } from 'vue'
const sentences = ref([
'欢迎使用TTS语音合成。',
'这是一个多语言支持的插件。',
'感谢你的使用。'
])
const currentIndex = ref(-1)
function speakWithHighlight() {
currentIndex.value = 0
readCurrent()
}
function readCurrent() {
if (currentIndex.value >= sentences.value.length) {
currentIndex.value = -1
console.log('全部读完')
return
}
ttsSpeak({
text: sentences.value[currentIndex.value],
onDone: () => {
currentIndex.value++
setTimeout(readCurrent, 200)
},
fail: (err) => {
console.error('朗读失败:', err.errMsg)
currentIndex.value++
readCurrent()
}
})
}
<!-- 模板中配合使用 -->
<view v-for="(s, i) in sentences" :key="i"
:style="{ color: i === currentIndex ? '#007aff' : '#333', fontWeight: i === currentIndex ? 'bold' : 'normal' }">
{{ s }}
</view>
场景 5:离开页面时清理
import { onUnload } from '@dcloudio/uni-app'
import { ttsStop, ttsShutdown } from '@/uni_modules/sehochen-tts'
onUnload(() => {
ttsStop() // 先停止正在播放的内容
ttsShutdown() // 再释放引擎资源
})
场景 6:引擎状态轮询
首次初始化是异步的,可以轮询等待就绪:
import { ttsGetEngineInfo, ttsSpeak } from '@/uni_modules/sehochen-tts'
function waitAndSpeak(text, maxRetry = 10) {
let retry = 0
const timer = setInterval(() => {
const info = ttsGetEngineInfo()
if (info.initStatus === 2) {
// 引擎就绪,开始播放
clearInterval(timer)
ttsSpeak({ text })
} else if (info.initStatus === -1 || retry >= maxRetry) {
// 初始化失败或超时
clearInterval(timer)
uni.showToast({ title: 'TTS 引擎初始化失败', icon: 'none' })
}
retry++
}, 300) // 每 300ms 检查一次
}
场景 7:多语言一键朗读
const langMap = {
zh: { code: 'zh-CN', text: '你好,世界' },
en: { code: 'en-US', text: 'Hello, world' },
ja: { code: 'ja-JP', text: 'こんにちは、世界' },
ko: { code: 'ko-KR', text: '안녕하세요, 세계' },
fr: { code: 'fr-FR', text: 'Bonjour le monde' },
de: { code: 'de-DE', text: 'Hallo Welt' },
es: { code: 'es-ES', text: 'Hola mundo' },
ru: { code: 'ru-RU', text: 'Привет, мир' },
it: { code: 'it-IT', text: 'Ciao mondo' },
pt: { code: 'pt-BR', text: 'Olá mundo' }
}
function speakLang(key) {
const item = langMap[key]
if (!item) return
ttsSpeak({
text: item.text,
lang: item.code,
fail: (err) => {
uni.showToast({
title: `${key} 不可用: ${err.errMsg}`,
icon: 'none',
duration: 2500
})
}
})
}
六、错误处理
6.1 错误码完整列表
| 错误码 | 常量含义 | 触发原因 | 解决方法 |
|---|---|---|---|
9020001 |
初始化失败 | 设备没有安装 TTS 引擎,或引擎初始化异常 | 安装 Google TTS / 讯飞语记 |
9020002 |
播放失败 | 引擎在播放过程中内部出错 | 重试或重启 APP |
9020003 |
语言不可用 | 指定语言的语言包未安装 | 去系统设置下载语言包 |
9020004 |
文本为空 | text 参数为空字符串或未传 |
确保传入有效文字 |
9020005 |
参数错误 | rate/pitch 不在 0.5~2.0 范围 |
检查参数值 |
6.2 推荐的错误处理写法
function speak(text, lang) {
ttsSpeak({
text,
lang,
fail: (err) => {
switch (err.errCode) {
case 9020001:
// TTS 引擎未安装
uni.showModal({
title: 'TTS 不可用',
content: '您的手机未安装文字转语音引擎,请安装 Google 文字转语音或讯飞语记。',
showCancel: false
})
break
case 9020003:
// 语言包未安装
uni.showModal({
title: '语言不支持',
content: `您的手机未安装 ${lang} 语言包。\n请前往 设置 → 语言和输入法 → 文字转语音 → 安装语音数据 下载。`,
showCancel: false
})
break
case 9020004:
// 没输入文字
uni.showToast({ title: '请输入要朗读的文字', icon: 'none' })
break
case 9020005:
// 参数错误
console.error('TTS 参数错误,请检查 rate/pitch 范围')
break
default:
// 其他错误
uni.showToast({ title: err.errMsg || '播放失败', icon: 'none', duration: 3000 })
}
}
})
}
6.3 防御性调用
避免在引擎未就绪时出错:
import { ttsGetEngineInfo, ttsSpeak } from '@/uni_modules/sehochen-tts'
function safeSpeak(text) {
const info = ttsGetEngineInfo()
if (info.initStatus === -1) {
uni.showToast({ title: 'TTS 引擎不可用', icon: 'none' })
return
}
// 即使 initStatus 为 0 或 1,ttsSpeak 也会自动处理初始化
ttsSpeak({
text,
fail: (err) => {
uni.showToast({ title: err.errMsg, icon: 'none' })
}
})
}
七、平台差异
Android
| 项目 | 说明 |
|---|---|
| 最低版本 | Android 5.0 (API 21) |
| 依赖 | 需要安装 TTS 引擎(Google TTS / 讯飞语记等) |
| 初始化 | 首次调用 ttsSpeak 时异步初始化,约 1~3 秒 |
| 语言包 | 不同语言可能需要单独下载 |
| 暂停/恢复 | 模拟实现(stop + 估算断点续播),非原生 API |
| 恢复精度 | 按时间估算(中文约 4 字/秒),短文本基本准确,长文本有 1~3 字偏差 |
| 最大输入 | 约 4000 字符(取决于引擎) |
| 注意 | 部分国产 ROM 可能限制了 TTS 功能 |
iOS
| 项目 | 说明 |
|---|---|
| 最低版本 | iOS 12.0 |
| 依赖 | 无需安装,系统内置 AVSpeechSynthesizer |
| 初始化 | 几乎瞬间完成 |
| 静音开关 | ⚠️ 硬件静音开关打开时没有声音! 这是 iOS 系统行为 |
| 语速映射 | 插件自动将 0.5~2.0 映射到 AVSpeechUtterance.rate 范围 |
| 暂停/恢复 | ✅ 原生支持,精确暂停/恢复 |
| 最大输入 | 无明确限制 |
鸿蒙
| 项目 | 说明 |
|---|---|
| 最低版本 | 鸿蒙 NEXT 4.0+ |
| 依赖 | 使用系统内置 CoreSpeechKit |
| 初始化 | 首次调用时异步初始化 |
| 语言包 | 取决于系统配置 |
| 暂停/恢复 | 模拟实现(同 Android),非原生 API |
八、常见问题排查
Q1: 完全没有声音?
按顺序排查:
- 手机媒体音量开了吗?(注意:不是铃声音量,是媒体音量)
- iOS 用户:检查侧边的硬件静音开关是否关闭(橙色=静音)
- Android 用户:去
设置 → 语言和输入法 → 文字转语音输出看看有没有安装引擎 - 监听
fail回调,看有没有错误信息 - 调用
ttsGetEngineInfo()看initStatus是否为2(已就绪)
Q2: 中文能读,英文/日文不行?
手机缺少对应语言的语音包。
- Android:
设置 → 语言和输入法 → 文字转语音输出 → 点引擎旁的齿轮 ⚙ → 安装语音数据 → 下载需要的语言 - iOS:
设置 → 辅助功能 → 朗读内容 → 声音 → 下载需要的语言
Q3: 怎么知道设备上有哪些 TTS 引擎?
import { ttsGetInstalledEngines } from '@/uni_modules/sehochen-tts'
const engines = ttsGetInstalledEngines()
console.log(engines) // 如 ["com.google.android.tts"]
Q4: 怎么知道引擎是否就绪?
import { ttsGetEngineInfo } from '@/uni_modules/sehochen-tts'
const info = ttsGetEngineInfo()
if (info.initStatus === 2) {
console.log('引擎已就绪,可以播放')
} else {
console.log('引擎状态:', info.initStatus, '(0=未初始化, 1=初始化中, -1=失败)')
}
Q5: 连续播放多段文字怎么做?
利用 onDone 回调串行播放。参见 场景 2:顺序朗读列表。
Q6: 为什么第一次播放要等一会儿?
Android TTS 引擎首次启动需要 1~3 秒初始化。后续播放不需要等待。
如果想在播放前确认引擎就绪,参见 场景 6:引擎状态轮询。
Q7: 暂停后恢复,位置准确吗?
| 平台 | 精度 |
|---|---|
| iOS | ✅ 精确,原生 API 支持 |
| Android | ⚠️ 按时间估算,短文本基本准确,长文本有 1~3 字偏差 |
| 鸿蒙 | ⚠️ 同 Android,按时间估算 |
Q8: 能同时读两段话吗?
不能。新播放会自动停掉旧的(QUEUE_FLUSH 模式)。
Q9: 为什么 ttsSpeak 后立刻 ttsStop 没效果?
首次调用 ttsSpeak 时引擎还在初始化中(异步),此时 ttsStop 可能因为还没开始播放而无效果。建议在 onStart 回调触发后再进行控制操作。
Q10: 页面退出时要不要手动释放?
建议在 onUnload 中调用 ttsStop() + ttsShutdown()。不调用也不会崩溃,但会占用一些内存。
九、Android 安装 TTS 引擎指南
国内手机(华为、小米、OPPO、vivo)通常自带 TTS 引擎,不需要额外安装。如果提示没有引擎,按以下步骤操作:
方法一:安装 Google 文字转语音
- 打开手机设置
- 找到语言和输入法(部分手机在"更多设置"或"系统设置"中)
- 点文字转语音输出(或"TTS"、"语音合成")
- 如果列表为空,去应用商店搜索 "Google 文字转语音" 安装
- 安装后回到第 3 步的页面,确认引擎已出现
- 点引擎旁的齿轮图标 ⚙ → 安装语音数据 → 下载需要的语言包
方法二:使用国内引擎
如果 Google 服务不可用,可以使用国内 TTS 引擎:
- 讯飞语记:在应用商店搜索安装
- 安装后在系统设置的"文字转语音输出"中选择它作为默认引擎
下载语言包
选中引擎后,点齿轮图标进入引擎设置,可以下载各种语言的语音数据:
- 中文(简体)- 通常预装
- 英语(美国)- 可能需要下载
- 日语、韩语等 - 按需下载
十、API 速查
| API | 做什么 | 参数 | 返回值 | 最简示例 |
|---|---|---|---|---|
ttsSpeak |
播放文字 | TTSSpeakOptions |
void |
ttsSpeak({ text: "你好" }) |
ttsStop |
停止播放 | 无 | void |
ttsStop() |
ttsPause |
暂停播放 | 无 | void |
ttsPause() |
ttsResume |
恢复播放 | 无 | void |
ttsResume() |
ttsGetEngineInfo |
查看引擎状态 | 无 | TTSEngineInfo |
const info = ttsGetEngineInfo() |
ttsGetInstalledEngines |
引擎列表 | 无 | string[] |
const list = ttsGetInstalledEngines() |
ttsShutdown |
释放引擎 | 无 | void |
ttsShutdown() |

收藏人数:
购买普通授权版(
试用
使用 HBuilderX 导入示例项目
赞赏(0)
下载 1
赞赏 0
下载 12304069
赞赏 1923
赞赏
京公网安备:11010802035340号