更新记录
1.0.0(2026-09-07)
- 首次发布
- 支持离线语音合成(内置度小宇/度小美/度逍遥/度丫丫 4 个发音人模型)
- 支持在线语音合成(永久 iamKey 鉴权)
- 支持 speak/synthesize/pause/resume/stop/release 完整控制接口
- 支持合成事件回调:开始/进度/音频数据/完成/错误
- 支持引擎与模型信息查询(getTTSInfo)
平台兼容性
uni-app(4.27)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| √ | √ | × | × | √ | √ | 6.0 | × | × |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| × | × | × | × | × | × | × | × | × | - | × | × |
uni-app x(3.8.5)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| - | - | 6.0 | × | - | - |
zy-baidutts 百度离线语音合成
基于百度语音合成 Android SDK 6.4.0(tts_1.0.3-9c93a23)封装的 UTS 插件(原生混编实现),支持纯离线语音合成与在线语音合成,内置 4 个离线发音人模型,无需联网即可完成语音播报。
功能特性
- 离线合成:插件内置 4 个发音人声学模型 + 中文文本模型,首次启动自动复制到应用私有目录,断网可用
- 在线合成:支持永久 iamKey 鉴权,一次配置长期使用
- 混合模式(mix):离线引擎与在线引擎同时加载,单次合成可指定模式
- 完整控制:speak(合成并播放)、synthesize(仅合成不播放)、pause(暂停)、resume(继续)、stop(停止)、release(释放资源)
- 按次切换发音人:单次合成可临时指定发音人,无需重新初始化
- 单次合成参数:语速/音量/音调可按次覆盖
- 音频数据回调:可获取合成音频(16K 16bits 单声道 PCM 的 Base64),用于保存文件或自定义播放
- 事件回调:合成开始、合成数据、播放进度、播放完成、错误等全流程事件
平台支持
| 平台 | 支持 |
|---|---|
| Android | ✔(minSdkVersion 21,Android 5.0+) |
| iOS | ✘ |
| Web / 小程序 / Harmony | ✘ |
环境要求
- HBuilderX 4.27 及以上版本(回调 keepAlive 特性依赖 4.27+)
- uni-app 项目(vue2 / vue3 均可)
- 必须打云端自定义基座:插件包含三方原生 SDK(AAR 约 10MB)与离线模型(约 55MB),标准基座不包含,本地标准基座运行无法生效
集成方式
- 将插件放入项目
uni_modules目录(或通过插件市场导入) - 在 HBuilderX 中选择「运行 → 运行到手机或模拟器 → 制作自定义调试基座」或「发行 → 原生 App-云打包」,勾选自定义基座
- 代码中导入使用:
import { initTTS, speak, synthesize, pauseTTS, resumeTTS, stopTTS, releaseTTS, getTTSInfo } from "@/uni_modules/zy-baidutts";
快速上手
import { initTTS, speak } from "@/uni_modules/zy-baidutts";
// 1. 初始化引擎(离线模式需要 appId + 序列号 SN)
initTTS({
mode: "offline",
appId: "您的AppId",
authSn: "您的序列号SN",
speaker: "duxiaomei",
speed: "5",
enableLog: false,
success: (res) => {
console.log("初始化成功,引擎版本=" + res.engineVersion);
console.log("文本模型:" + res.textModelPath);
console.log("声学模型:" + res.speechModelPath);
},
fail: (err) => {
console.log("初始化失败:" + err.errMsg + " (errCode=" + err.errCode + ")");
}
});
// 2. 合成并播放
speak({
text: "你好,世界",
onStart: (res) => {
console.log("合成开始,utteranceId=" + res.utteranceId);
},
: (res) => {
console.log("播放进度:" + res.progress + "%");
},
onFinish: (res) => {
console.log("播放完成,engineType=" + (res.engineType === 0 ? "在线" : "离线"));
},
onError: (res) => {
console.log("合成出错:" + res.errorMessage);
}
});
鉴权说明
| 模式 | 鉴权要求 | 说明 |
|---|---|---|
| offline | appId + authSn(序列号 SN) |
需在百度智能云控制台领取离线合成资源并购买/申请序列号。appId、应用包名、SN 三者必须与控制台登记完全一致,否则报 -102/-109 授权错误。首次联网自动下载授权文件 |
| online | iamKey(永久授权 key) |
使用官网申请的永久 iamKey,每次启动 App 激活一次 |
| mix | 两者都需要 | 离线引擎与在线引擎分别鉴权 |
内置离线模型
插件 utssdk/app-android/assets/ 目录内置以下模型(共约 55MB),首次初始化自动复制到应用私有目录 filesDir/tts_models/(只复制一次,之后直接使用):
| 发音人标识 | 说明 | 模型文件 | 大小 |
|---|---|---|---|
duxiaomei |
度小美(普通女声,默认) | bd_etts_common_speech_duxiaomei_mand_eng_high_am-tac-csubgan16k_v4.9.0_20240918_20251031153737.dat | 10.7MB |
duxiaoyu |
度小宇(成熟男声) | bd_etts_common_speech_duxiaoyu_mand_eng_high_am-tac-csubgan16k_v4.9.0_20240918_20251031153737.dat | 10.3MB |
duxiaoyao |
度逍遥(情感男声) | bd_etts_common_speech_duxiaoyao_mand_eng_high_am-tac-csubgan16k_v4.9.0_20251110_20251210203318.dat | 10.3MB |
duyaya |
度丫丫(可爱女童) | bd_etts_common_speech_duyaya_mand_eng_high_am-tac-csubgan16k_v4.9.0_20220419_20251031153737.dat | 8.3MB |
中文文本模型(所有发音人共用):bd_etts_common_text_txt_all_mand_eng_middle_big_v6.0.0_20240731.dat(15.5MB)
如需替换为其他百度模型,将模型文件放入插件 assets 目录,并通过
initTTS的textModelPath/speechModelPath指定文件绝对路径即可。
API 详细说明
initTTS(options) 初始化语音合成引擎
初始化流程:创建合成器实例 → 复制离线模型(异步)→ 设置鉴权与参数 → 加载引擎。重复调用会先释放旧实例再重新初始化。
参数 TTSInitOptions:
| 属性 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| mode | string | 否 | "offline" | 合成模式:"offline" 纯离线 / "online" 纯在线 / "mix" 离在线混合 |
| appId | string | 离线/mix 必填 | - | 百度开放平台应用 AppId |
| authSn | string | 离线/mix 必填 | - | 离线鉴权序列号 SN |
| iamKey | string | 在线/mix 必填 | - | 在线鉴权永久 iamKey |
| speaker | string | 否 | "duxiaomei" | 默认离线发音人:"duxiaoyu" / "duxiaomei" / "duxiaoyao" / "duyaya" |
| textModelPath | string | 否 | 内置模型 | 自定义文本模型文件绝对路径(与 speechModelPath 需同时提供,提供后不再复制内置模型) |
| speechModelPath | string | 否 | 内置模型 | 自定义声学模型文件绝对路径 |
| speed | string | 否 | "5" | 语速,取值 ["0"-"15"] |
| volume | string | 否 | "5" | 音量,取值 ["0"-"15"] |
| pitch | string | 否 | "5" | 音调,取值 ["0"-"15"] |
| onlineSpeaker | string | 否 | "4100" | 在线发音人编号(仅在线/混合模式生效) |
| enableLog | boolean | 否 | false | 是否开启 SDK 调试日志(排查问题时开启) |
| success | function | 否 | - | 初始化成功回调 (res: TTSInitResult) => void |
| fail | function | 否 | - | 初始化失败回调 (err: TTSFail) => void |
| complete | function | 否 | - | 结束回调(成功/失败均触发) |
TTSInitResult:
| 属性 | 类型 | 说明 |
|---|---|---|
| mode | string | 实际初始化的模式 |
| engineVersion | number | 引擎版本号 |
| engineInfo | string | 引擎信息描述 |
| textModelPath | string | 文本模型文件绝对路径 |
| speechModelPath | string | 声学模型文件绝对路径 |
| offlineCode | number | 离线引擎加载结果码(0 为成功;mode=online 时为 0) |
| offlineMessage | string | 离线引擎加载结果描述 |
| onlineCode | number | 在线引擎加载结果码(0 为成功;mode=offline 时为 0) |
| onlineMessage | string | 在线引擎加载结果描述 |
speak(options) 合成并播放
将文本加入合成队列,合成完成后自动播放。支持多次调用,SDK 内部按顺序排队播放。
参数 TTSSpeakOptions:
| 属性 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| text | string | 是 | - | 待合成文本,不超过 120 GBK 字节(约 60 个汉字),超长请分句多次调用 |
| mode | string | 否 | 初始化时的模式 | 本次合成模式:"offline" / "online" / "mix" |
| utteranceId | string | 否 | 自动生成 | 合成标识 id(不传自动生成 tts_utt_N),用于关联事件回调 |
| speed | string | 否 | 初始化值 | 本次合成语速 ["0"-"15"] |
| volume | string | 否 | 初始化值 | 本次合成音量 ["0"-"15"] |
| pitch | string | 否 | 初始化值 | 本次合成音调 ["0"-"15"] |
| speaker | string | 否 | 初始化值 | 本次离线发音人标识。切换为非默认发音人时,插件会自动复制对应声学模型(首次) |
| needAudioData | boolean | 否 | false | 是否在 onAudioData 回调中返回音频数据(Base64 编码 PCM) |
| onStart | function | 否 | - | 合成开始回调 (res: TTSEvent) => void |
| onProgress | function | 否 | - | 播放进度回调,progress 取值 0-100 |
| onAudioData | function | 否 | - | 合成音频数据回调(needAudioData 为 true 时有效) |
| onFinish | function | 否 | - | 合成播放完成回调 |
| onError | function | 否 | - | 合成/播放出错回调 |
| success | function | 否 | - | 入队成功回调 (res: TTSSpeakResult) => void |
| fail | function | 否 | - | 入队失败回调(文本为空、引擎未初始化、合成失败等) |
| complete | function | 否 | - | 结束回调 |
TTSSpeakResult:
| 属性 | 类型 | 说明 |
|---|---|---|
| utteranceId | string | 本次合成标识 id |
| code | number | 入队结果码(0 为成功) |
| message | string | 入队结果描述 |
注意:
success仅表示任务入队成功,合成与播放结果请通过onStart/onProgress/onFinish/onError事件回调获取。
synthesize(options) 仅合成不播放
参数与 speak 完全一致,区别是不自动播放,仅完成合成(音频数据可通过 onAudioData 获取,采样率等信息在 onFinish 的 sampleRate 字段)。
pauseTTS / resumeTTS / stopTTS / releaseTTS 播放控制
// 暂停播放(之后可通过 resumeTTS 恢复)
pauseTTS({ success: (res) => console.log(res.code) });
// 继续播放(只能恢复被"暂停"的任务)
resumeTTS();
// 停止合成并停止播放(彻底终止,不可恢复!)
stopTTS();
// 释放引擎资源(彻底销毁实例,需重新 initTTS 才能继续使用)
releaseTTS();
控制接口参数均为可选 TTSControlOptions:
| 属性 | 类型 | 说明 |
|---|---|---|
| success | function | 控制成功回调 (res: TTSControlResult) => void,res 包含 code(0 成功)与 message |
| fail | function | 控制失败回调 |
| complete | function | 结束回调 |
三个接口的语义区别(重要):
| 接口 | 行为 | 后续可否 resume |
|---|---|---|
| pauseTTS | 暂停当前播放 | ✔ resumeTTS 从暂停处恢复 |
| stopTTS | 彻底终止合成与播放,丢弃队列 | ✘ 不可恢复,需重新 speak |
| releaseTTS | 销毁引擎实例并释放资源 | ✘ 需重新 initTTS |
getTTSInfo(options) 查询引擎/模型信息
参数 TTSInfoOptions:
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| modelPath | string | 否 | 需要校验/查询的模型文件绝对路径(不传则不校验模型) |
| success | function | 否 | (res: TTSInfoResult) => void |
TTSInfoResult:
| 属性 | 类型 | 说明 |
|---|---|---|
| engineVersion | number | 引擎版本号 |
| engineInfo | string | 引擎信息描述 |
| cuid | string | 设备唯一标识(离线鉴权与 cuid 绑定相关) |
| modelInfo | string | 模型文件信息(modelPath 为空时为空字符串) |
| modelValid | boolean | 模型文件是否有效(modelPath 为空时为 false) |
事件类型(TTSEvent)
所有事件回调的参数均为 TTSEvent 对象:
| 属性 | 类型 | 说明 |
|---|---|---|
| type | string | 事件类型,见下表 |
| utteranceId | string | 合成标识 id |
| sn | string | 合成序列号 |
| instanceId | string | 实例 id |
| progress | number | 合成/播放进度(0-100) |
| engineType | number | 合成引擎类型:0 在线,1 离线 |
| sampleRate | number | 当前音频采样率 |
| errorCode | number | 错误码(无错误为 0) |
| errorMessage | string | 错误信息 |
| audioData | string | 音频数据(16K 16bits 单声道 PCM,Base64 编码;needAudioData 为 false 时为空字符串) |
type 取值:
| 事件类型 | 触发时机 | 分发至 |
|---|---|---|
| SYNTHESIZE_START | 单条合成开始 | onStart |
| SYNTHESIZE_DATA_ARRIVED | 合成音频数据到达(一段) | onAudioData |
| SYNTHESIZE_FINISH | 单条合成完成 | onFinish |
| SYNTHESIZE_ERROR | 合成出错 | onError |
| PLAY_START | 播放开始 | onStart |
| PLAY_PROGRESS | 播放进度变化 | onProgress |
| PLAY_FINISH | 播放完成 | onFinish |
| PLAY_ERROR | 播放出错 | onError |
保存音频文件示例:将
audioDataBase64 解码后按顺序写入同一文件,即可得到完整 PCM;文件扩展名建议.pcm,16K 采样率 16bit 单声道。
插件错误码
插件自身错误码(errCode),错误对象为 TTSFail(含 errSubject="zy-baidutts"、errCode、errMsg):
| errCode | 说明 |
|---|---|
| 9060001 | 引擎未初始化,请先调用 initTTS |
| 9060002 | 参数错误(text 为空、无法获取 Context 等) |
| 9060003 | 引擎初始化失败(实例创建失败或引擎加载失败) |
| 9060004 | 模型文件不存在或无效(发音人标识无效、模型校验失败) |
| 9060005 | 合成失败(speak/synthesize 入队失败) |
| 9060006 | 播放控制失败(pause/resume/stop/release 返回错误) |
| 9060007 | 鉴权参数缺失(离线需要 appId + authSn,在线需要 iamKey) |
| 9060008 | 模型文件复制失败(assets → 私有目录) |
合成/播放过程中的引擎级错误(如授权失败 -102/-109 等)通过 onError 事件的 errorCode / errorMessage 返回,详见百度官方文档《语音合成 Android SDK》错误码章节。
示例代码
<template>
<view class="page">
<view class="header">
<text class="app-title">百度离线语音合成</text>
<text class="app-subtitle">支持离线合成(内置4发音人)与在线合成</text>
</view>
<view class="section">
<view class="section-title">鉴权配置</view>
<input class="input" v-model="appId" placeholder="AppId(离线合成必填)" placeholder-class="ph" />
<input class="input" v-model="authSn" placeholder="序列号 SN(离线合成必填)" placeholder-class="ph" />
<input class="input" v-model="iamKey" placeholder="永久 iamKey(在线合成必填)" placeholder-class="ph" />
<view class="mode-row">
<view class="mode-item" :class="{ active: mode === 'offline' }" @click="mode = 'offline'">离线</view>
<view class="mode-item" :class="{ active: mode === 'online' }" @click="mode = 'online'">在线</view>
<view class="mode-item" :class="{ active: mode === 'mix' }" @click="mode = 'mix'">混合</view>
</view>
</view>
<view class="section">
<view class="section-title">发音人与语速</view>
<view class="mode-row">
<view class="mode-item" :class="{ active: speaker === 'duxiaomei' }" @click="speaker = 'duxiaomei'">度小美</view>
<view class="mode-item" :class="{ active: speaker === 'duxiaoyu' }" @click="speaker = 'duxiaoyu'">度小宇</view>
<view class="mode-item" :class="{ active: speaker === 'duxiaoyao' }" @click="speaker = 'duxiaoyao'">度逍遥</view>
<view class="mode-item" :class="{ active: speaker === 'duyaya' }" @click="speaker = 'duyaya'">度丫丫</view>
</view>
<view class="slider-row">
<text class="slider-label">语速 {{ speed }}</text>
<slider class="slider" :min="0" :max="15" :step="1" :value="speed" activeColor="#4f6ef7" @change="onSpeedChange" />
</view>
</view>
<view class="section">
<view class="section-title">合成文本</view>
<textarea class="textarea" v-model="text" placeholder="请输入待合成文本(不超过60个汉字)" placeholder-class="ph"></textarea>
<view class="progress-row" v-if="playing">
<text class="progress-label">播放进度:{{ progress }}%</text>
</view>
</view>
<view class="btn-grid">
<button class="btn primary" @click="handleInit">初始化</button>
<button class="btn success" @click="handleSpeak">合成播放</button>
<button class="btn info" @click="handleSynthesize">仅合成</button>
<button class="btn warn" @click="handlePause">暂停</button>
<button class="btn warn" @click="handleResume">继续</button>
<button class="btn warn" @click="handleStop">停止</button>
<button class="btn danger" @click="handleRelease">释放</button>
<button class="btn info" @click="handleInfo">引擎信息</button>
</view>
<view class="section">
<view class="section-title">日志</view>
<scroll-view class="log-area" scroll-y>
<text class="log-text">{{ logs }}</text>
</scroll-view>
</view>
</view>
</template>
<script>
import { initTTS, speak, synthesize, pauseTTS, resumeTTS, stopTTS, releaseTTS, getTTSInfo } from "@/uni_modules/zy-baidutts";
export default {
data() {
return {
appId: '',
authSn: '',
iamKey: '',
mode: 'offline',
speaker: 'duxiaomei',
speed: 5,
text: '百度语音合成,让应用开口说话。',
logs: '',
playing: false,
progress: 0
}
},
methods: {
log(msg) {
const now = new Date();
const time = now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds();
this.logs = '[' + time + '] ' + msg + '\n' + this.logs;
},
onSpeedChange(e) {
this.speed = e.detail.value;
},
handleInit() {
if (this.mode === 'offline' && (!this.appId || !this.authSn)) {
this.log('离线合成需要填写 AppId 和序列号 SN');
return;
}
if (this.mode === 'online' && !this.iamKey) {
this.log('在线合成需要填写永久 iamKey');
return;
}
this.log('开始初始化(首次启动需复制模型文件,请耐心等待)...');
initTTS({
mode: this.mode,
appId: this.appId,
authSn: this.authSn,
iamKey: this.iamKey,
speaker: this.speaker,
speed: String(this.speed),
enableLog: true,
success: (res) => {
this.log('初始化成功 mode=' + res.mode + ' engineVersion=' + res.engineVersion);
this.log('文本模型: ' + res.textModelPath);
this.log('声学模型: ' + res.speechModelPath);
},
fail: (err) => {
this.log('初始化失败: ' + err.errMsg + ' (errCode=' + err.errCode + ')');
}
});
},
handleSpeak() {
this.playing = true;
this.progress = 0;
speak({
text: this.text,
onStart: (res) => {
this.log('合成开始 utteranceId=' + res.utteranceId);
},
: (res) => {
this.progress = res.progress;
},
onFinish: (res) => {
this.playing = false;
this.log('播放完成 engineType=' + (res.engineType === 0 ? '在线' : '离线'));
},
onError: (res) => {
this.playing = false;
this.log('合成出错: ' + res.errorMessage + ' (code=' + res.errorCode + ')');
},
fail: (err) => {
this.playing = false;
this.log('调用失败: ' + err.errMsg + ' (errCode=' + err.errCode + ')');
}
});
},
handleSynthesize() {
synthesize({
text: this.text,
onFinish: (res) => {
this.log('合成完成(未播放),采样率=' + res.sampleRate);
},
onError: (res) => {
this.log('合成出错: ' + res.errorMessage + ' (code=' + res.errorCode + ')');
},
fail: (err) => {
this.log('调用失败: ' + err.errMsg);
}
});
},
handlePause() {
pauseTTS({
success: (res) => this.log('已暂停'),
fail: (err) => this.log('暂停失败: ' + err.errMsg)
});
},
handleResume() {
resumeTTS({
success: (res) => this.log('已继续'),
fail: (err) => this.log('继续失败: ' + err.errMsg)
});
},
handleStop() {
stopTTS({
success: (res) => {
this.playing = false;
this.log('已停止');
},
fail: (err) => this.log('停止失败: ' + err.errMsg)
});
},
handleRelease() {
releaseTTS({
success: (res) => this.log('资源已释放 code=' + res.code),
fail: (err) => this.log('释放失败: ' + err.errMsg)
});
},
handleInfo() {
getTTSInfo({
success: (res) => {
this.log('引擎版本=' + res.engineVersion);
this.log('引擎信息=' + res.engineInfo);
this.log('设备 cuid=' + res.cuid);
},
fail: (err) => this.log('查询失败: ' + err.errMsg)
});
}
}
}
</script>
<style>
.page { min-height: 100vh; background: #f0f2f5; padding-bottom: 40rpx; }
.header { padding: 60rpx 40rpx 40rpx; background: linear-gradient(135deg, #4f6ef7 0%, #6c5ce7 100%); }
.app-title { display: block; color: #ffffff; font-size: 40rpx; font-weight: bold; }
.app-subtitle { display: block; color: rgba(255,255,255,0.85); font-size: 24rpx; margin-top: 10rpx; }
.section { background: #ffffff; border-radius: 16rpx; margin: 24rpx; padding: 24rpx; }
.section-title { font-size: 30rpx; font-weight: bold; color: #333333; margin-bottom: 20rpx; }
.input { border: 1px solid #e0e0e0; border-radius: 8rpx; padding: 16rpx 20rpx; font-size: 28rpx; margin-bottom: 16rpx; }
.ph { color: #bbbbbb; }
.mode-row { display: flex; flex-direction: row; flex-wrap: wrap; }
.mode-item { padding: 12rpx 30rpx; border-radius: 32rpx; background: #f0f2f5; color: #666666; font-size: 26rpx; margin: 0 16rpx 16rpx 0; }
.mode-item.active { background: #4f6ef7; color: #ffffff; }
.slider-row { display: flex; flex-direction: row; align-items: center; }
.slider-label { width: 160rpx; font-size: 26rpx; color: #666666; }
.slider { flex: 1; }
.textarea { width: 100%; height: 160rpx; border: 1px solid #e0e0e0; border-radius: 8rpx; padding: 16rpx 20rpx; font-size: 28rpx; box-sizing: border-box; }
.progress-row { margin-top: 16rpx; }
.progress-label { font-size: 26rpx; color: #4f6ef7; }
.btn-grid { display: flex; flex-direction: row; flex-wrap: wrap; justify-content: space-between; margin: 24rpx; }
.btn { width: 23%; margin-bottom: 16rpx; font-size: 26rpx; line-height: 2.4; }
.btn.primary { background: #4f6ef7; color: #ffffff; }
.btn.success { background: #11998e; color: #ffffff; }
.btn.info { background: #6c5ce7; color: #ffffff; }
.btn.warn { background: #f39c12; color: #ffffff; }
.btn.danger { background: #e74c3c; color: #ffffff; }
.log-area { height: 300rpx; background: #1e1e28; border-radius: 12rpx; padding: 16rpx; }
.log-text { color: #9ae6b4; font-size: 22rpx; font-family: monospace; white-space: pre-wrap; }
</style>
注意事项
- 自定义基座:插件依赖原生 SDK(AAR)与 55MB 模型资源,必须云端打包自定义基座后运行,标准基座无效。
- 首次初始化耗时:首次启动需复制约 55MB 模型文件到私有目录,耗时数秒(异步执行,不阻塞 UI),仅执行一次。
- 文本长度限制:单次合成文本不超过 120 GBK 字节(约 60 个汉字)。长文本请分句后多次调用
speak,SDK 内部会排队依次播放。 - stop 与 pause 的区别:
stopTTS会彻底终止任务,之后调用resumeTTS无效;需要"暂停后继续"请使用pauseTTS。 - 回调生命周期(keepAlive):
speak/synthesize的事件回调(/onAudioData 等)需持续触发,插件已启用@UTSJS.keepAlive,回调对象常驻内存。请避免高频循环调用 speak(如 for 循环中短间隔调用),正常业务频率无影响。 - 发音人切换:
speak时传入非默认speaker,插件会自动将对应声学模型复制到私有目录(首次约需 1 秒)后再入队合成。 - 鉴权一致性:离线授权校验 appId、应用包名、SN 三者匹配,打包时请确认云打包使用的 Android 包名与百度控制台登记一致。
- 在线模式数据安全:在线/混合模式下,待合成文本会发送至百度语音服务器;纯离线模式文本仅本地处理。
常见问题(FAQ)
Q1:初始化报 -102 / -109 授权错误? A:检查 appId、SN 与百度控制台登记信息是否一致;确认云打包的 Android 包名与控制台登记包名完全一致;设备首次使用需联网激活一次授权。
Q2:初始化报 9060008 模型复制失败?
A:确认插件 utssdk/app-android/assets/ 目录下 5 个 .dat 模型文件完整(文本模型 1 个 + 声学模型 4 个),重新打包基座后重试。
Q3:调用 resumeTTS 显示成功但没有继续播放?
A:resumeTTS 只能恢复被 pauseTTS 暂停的任务。若之前调用的是 stopTTS(彻底终止),resume 无法恢复,请重新调用 speak。
Q4: 只触发一次或报"回调函数已释放"?
A:HBuilderX 4.25+ 回调默认单次触发后回收。本插件 speak/synthesize 已配置 @UTSJS.keepAlive 支持持续回调,请确认 HBuilderX 版本 ≥ 4.27。
Q5:支持后台播放吗? A:合成与播放基于 SDK 内部线程,App 切后台通常可继续播放,但系统回收进程后无法保证;如需长时后台播报,请配合前台服务使用。
Q6:如何获取合成音频保存为文件?
A:speak/synthesize 时设置 needAudioData: true,在 onAudioData 回调中收集 audioData(Base64),全部到齐后(SYNTHESIZE_FINISH)Base64 解码写入文件,即为 16K 16bits 单声道 PCM 数据。

收藏人数:
购买源码授权版(
试用
赞赏(0)
下载 39
赞赏 0
下载 12566955
赞赏 1949
赞赏
京公网安备:11010802035340号