文章目录
- 前言
-
- 项目信息
- 一、前期准备
-
- [1.1 申请百度智能云账号](#1.1 申请百度智能云账号)
- [1.2 了解百度 TTS API](#1.2 了解百度 TTS API)
- 二、项目结构
- 三、核心代码实现
-
- [3.1 配置文件 (`src/config/tts.config.ts`)](#3.1 配置文件 (
src/config/tts.config.ts)) - [3.2 TTS 服务封装 (`src/utils/baiduTTS.ts`)](#3.2 TTS 服务封装 (
src/utils/baiduTTS.ts)) - [3.3 音频播放组件 (`src/components/AudioPlayer.vue`)](#3.3 音频播放组件 (
src/components/AudioPlayer.vue))
- [3.1 配置文件 (`src/config/tts.config.ts`)](#3.1 配置文件 (
- 四、遇到的坑及解决方案
-
- [坑 1:浏览器 CORS 错误 ❌](#坑 1:浏览器 CORS 错误 ❌)
- [坑 2:TTS 服务未初始化 ❌](#坑 2:TTS 服务未初始化 ❌)
- [坑 3:模拟器可以,真机不行 ❌](#坑 3:模拟器可以,真机不行 ❌)
- [坑 4:API 返回错误 -1100 ❌](#坑 4:API 返回错误 -1100 ❌)
- [坑 5:音频生成成功但播放失败 ❌ (最关键!)](#坑 5:音频生成成功但播放失败 ❌ (最关键!))
- 五、完整的开发流程
-
- [5.1 浏览器开发(H5)](#5.1 浏览器开发(H5))
- [5.2 小程序开发](#5.2 小程序开发)
- [5.3 生产发布](#5.3 生产发布)
- 六、测试检查清单
- 七、性能优化建议
-
- [7.1 音频缓存](#7.1 音频缓存)
- [7.2 预生成音频](#7.2 预生成音频)
- [7.3 使用 CDN](#7.3 使用 CDN)
- [八、常见问题 FAQ](#八、常见问题 FAQ)
-
- [Q1: 为什么模拟器可以,真机不行?](#Q1: 为什么模拟器可以,真机不行?)
- [Q2: 如何查看真机上的错误日志?](#Q2: 如何查看真机上的错误日志?)
- [Q3: API Key 有使用限制吗?](#Q3: API Key 有使用限制吗?)
- [Q4: 可以更换发音人吗?](#Q4: 可以更换发音人吗?)
- [Q5: 如何处理长文本?](#Q5: 如何处理长文本?)
- 九、相关资源
- 十、总结
前言
在小程序开发中,需要做一个文本播放功能,这里是英语播放。可以使用各大厂商的TTS播放服务。
项目信息
- 项目类型:uni-app(Vue3 + TypeScript)
- 目标平台:微信小程序
- TTS 服务:百度智能云语音合成
- 完成时间:2025-12-14
一、前期准备
1.1 申请百度智能云账号
- 访问 百度智能云
- 注册并登录账号
- 进入控制台 → 产品服务 → 人工智能 → 语音技术
- 创建应用,获取 API Key 和 Secret Key
1.2 了解百度 TTS API
- 官方文档:https://cloud.baidu.com/doc/SPEECH/s/Jkqq0b3uw
- API 端点:
- Token 获取:
https://aip.baidubce.com/oauth/2.0/token - 语音合成:
https://tsn.baidu.com/text2audio
- Token 获取:
二、项目结构
js
my-vue3-project/
├── src/
│ ├── config/
│ │ └── tts.config.ts # TTS 配置文件
│ ├── utils/
│ │ └── baiduTTS.ts # TTS 服务封装
│ ├── components/
│ │ └── AudioPlayer.vue # 音频播放组件
│ └── main.ts # TTS 初始化
└── server/
├── package.json
└── tts-proxy.js # 代理服务器(可选)
三、核心代码实现
3.1 配置文件 (src/config/tts.config.ts)
typescript
export const BAIDU_TTS_CONFIG = {
apiKey: '你的API_KEY',
secretKey: '你的SECRET_KEY',
useProxy: false, // 浏览器开发时设为 true
proxyUrl: 'http://localhost:3000/api/tts',
};
export const DEFAULT_TTS_OPTIONS = {
lang: 'en' as const,
spd: 5, // 语速
pit: 5, // 音调
vol: 8, // 音量
per: 0, // 发音人(0=女声,1=男声)
};
3.2 TTS 服务封装 (src/utils/baiduTTS.ts)
关键实现要点:
typescript
class BaiduTTSService {
// 1. 获取 Access Token(带缓存)
private async getAccessToken(): Promise<string> {
if (this.accessToken && Date.now() < this.tokenExpireTime) {
return this.accessToken;
}
// 请求新 token...
}
// 2. 文字转语音(使用 POST 方式)
async textToSpeech(options: TTSOptions): Promise<string> {
const token = await this.getAccessToken();
// 使用 POST 方式请求
const response = await uni.request({
url: 'https://tsn.baidu.com/text2audio',
method: 'POST',
data: { tex, tok, cuid, lan, spd, pit, vol, per, aue },
header: { 'Content-Type': 'application/x-www-form-urlencoded' },
responseType: 'arraybuffer',
});
// 3. 保存为临时文件(关键!)
const base64 = uni.arrayBufferToBase64(response.data);
const fs = uni.getFileSystemManager();
const tempFilePath = `${wx.env.USER_DATA_PATH}/tts_${Date.now()}.mp3`;
fs.writeFileSync(tempFilePath, base64, 'base64');
return tempFilePath; // 返回文件路径,不是 Base64
}
}
3.3 音频播放组件 (src/components/AudioPlayer.vue)
js
<script setup>
import { getTTSInstance, initBaiduTTS } from '../utils/baiduTTS';
import { BAIDU_TTS_CONFIG } from '../config/tts.config';
const generateAudio = async () => {
// 确保 TTS 服务已初始化
let tts;
try {
tts = getTTSInstance();
} catch (e) {
tts = initBaiduTTS(BAIDU_TTS_CONFIG);
}
// 生成音频
const audioUrl = await tts.textToSpeech({
text: props.text,
lang: 'en',
});
// 初始化播放器
innerAudioContext = uni.createInnerAudioContext();
innerAudioContext.src = audioUrl; // 使用临时文件路径
innerAudioContext.play();
};
</script>
四、遇到的坑及解决方案
坑 1:浏览器 CORS 错误 ❌
问题:
json
Access to XMLHttpRequest at 'https://aip.baidubce.com/...'
has been blocked by CORS policy
原因:浏览器的同源策略阻止跨域请求
解决方案:
- 开发阶段 :使用代理服务器
- 创建 Node.js 代理服务器(见
server/tts-proxy.js) - 配置
useProxy: true
- 创建 Node.js 代理服务器(见
- 生产环境 :
- 小程序:配置合法域名白名单
- App:直接调用 API
- Web:部署代理服务器
坑 2:TTS 服务未初始化 ❌
问题:
js
Error: 请先调用 initBaiduTTS 初始化服务
原因:组件加载时 TTS 服务还未初始化完成
解决方案:
js
// 在组件中动态初始化
let tts;
try {
tts = getTTSInstance();
} catch (e) {
console.log('TTS 服务未初始化,正在初始化...');
tts = initBaiduTTS(BAIDU_TTS_CONFIG);
}
坑 3:模拟器可以,真机不行 ❌
问题:微信开发者工具模拟器中正常,真机预览失败
原因:
- 真机预览会检查域名白名单
- 模拟器勾选"不校验合法域名"后可以访问任何域名
解决方案:
- 临时方案:使用"真机调试"而不是"预览"
- 正式方案 :在微信公众平台配置合法域名
-
登录 微信公众平台
-
开发 → 开发管理 → 开发设置 → 服务器域名
-
添加 request 合法域名:
https://aip.baidubce.com https://tsn.baidu.com
-
坑 4:API 返回错误 -1100 ❌
问题:
js
INNERERRORCODE:-1100, ERRMSG:在此服务器上找不到所请求的URL
errCode: 10001
原因:
- 使用 GET 方式请求,参数可能被截断
- 参数类型不正确(字符串 vs 数字)
解决方案:
typescript
// ❌ 错误:使用 GET 方式
uni.request({
url: `https://tsn.baidu.com/text2audio?${queryString}`,
method: 'GET',
});
// ✅ 正确:使用 POST 方式
uni.request({
url: 'https://tsn.baidu.com/text2audio',
method: 'POST',
data: {
tex: text,
tok: token,
spd: 5, // 数字类型,不是字符串
// ...
},
header: {
'Content-Type': 'application/x-www-form-urlencoded'
},
});
坑 5:音频生成成功但播放失败 ❌ (最关键!)
问题:
[AudioPlayer Error]
type: "AudioPlayerError"
原因 :小程序的 innerAudioContext 在真机上不支持直接播放 Base64 格式的音频
错误做法:
typescript
// ❌ 直接返回 Base64
const base64 = uni.arrayBufferToBase64(response.data);
return `data:audio/mp3;base64,${base64}`;
正确做法:
typescript
// ✅ 保存为临时文件
const base64 = uni.arrayBufferToBase64(response.data);
const fs = uni.getFileSystemManager();
const tempFilePath = `${wx.env.USER_DATA_PATH}/tts_${Date.now()}.mp3`;
fs.writeFileSync(tempFilePath, base64, 'base64');
return tempFilePath; // 返回文件路径
为什么这样做:
- Base64 URL 在真机上播放不稳定
- 临时文件路径更可靠
- 系统会自动清理临时文件
五、完整的开发流程
5.1 浏览器开发(H5)
bash
# 1. 启动代理服务器
cd server
npm install
npm start
# 2. 修改配置
# src/config/tts.config.ts
useProxy: true
# 3. 启动开发服务器
npm run dev:h5
5.2 小程序开发
bash
# 1. 构建小程序
npm run dev:mp-weixin
# 2. 配置
# src/config/tts.config.ts
useProxy: false
# 3. 微信开发者工具
# - 导入项目:dist/dev/mp-weixin
# - 详情 → 本地设置 → 勾选"不校验合法域名"
# - 使用"真机调试"测试
5.3 生产发布
bash
# 1. 配置合法域名(微信公众平台)
https://aip.baidubce.com
https://tsn.baidu.com
# 2. 构建生产版本
npm run build:mp-weixin
# 3. 上传代码
# 微信开发者工具 → 上传
# 4. 提交审核
# 微信公众平台 → 版本管理 → 提交审核
六、测试检查清单
开发阶段
- 模拟器中能正常播放
- 真机调试能正常播放
- 切换句子时音频正常切换
- 网络错误时有友好提示
- 音频加载时显示加载状态
发布前
- 已配置合法域名白名单
- 真机预览(不是调试)能正常播放
- API Key 和 Secret Key 正确
- 测试不同网络环境(WiFi、4G)
- 测试不同机型(iOS、Android)
七、性能优化建议
7.1 音频缓存
typescript
// 缓存已生成的音频
const audioCache = new Map<string, string>();
async textToSpeech(options: TTSOptions): Promise<string> {
const cacheKey = `${options.text}_${options.lang}`;
if (audioCache.has(cacheKey)) {
return audioCache.get(cacheKey)!;
}
const audioUrl = await this.generateAudio(options);
audioCache.set(cacheKey, audioUrl);
return audioUrl;
}
7.2 预生成音频
对于固定的句子,可以预先生成所有音频:
bash
node scripts/generateAudio.js
7.3 使用 CDN
将生成的音频文件上传到 CDN,直接使用 URL。
八、常见问题 FAQ
Q1: 为什么模拟器可以,真机不行?
A: 真机预览会检查域名白名单。使用"真机调试"或配置合法域名。
Q2: 如何查看真机上的错误日志?
A: 使用"真机调试"模式,在开发者工具的"调试器"标签中查看。
Q3: API Key 有使用限制吗?
A: 百度 TTS 有免费额度,超出后需要付费。查看控制台了解详情。
Q4: 可以更换发音人吗?
A: 可以,修改 per 参数:
- 0:女声
- 1:男声
- 3:情感合成-度逍遥
- 4:情感合成-度丫丫
Q5: 如何处理长文本?
A: 百度 TTS 单次请求有字符限制(约 1024 字节),长文本需要分段处理。
九、相关资源
官方文档
项目文档
BAIDU_TTS_SETUP.md- 基础设置指南CORS_TTS_SOLUTION.md- CORS 问题解决方案MINIPROGRAM_TTS_TROUBLESHOOTING.md- 小程序故障排查TTS_QUICK_START.md- 快速开始指南
十、总结
关键要点
- ✅ 使用 POST 方式请求 TTS API
- ✅ 保存为临时文件而不是使用 Base64
- ✅ 配置域名白名单才能在真机预览中使用
- ✅ 使用真机调试查看详细日志
- ✅ Token 缓存避免频繁请求
最终效果
- 模拟器:✅ 正常播放
- 真机调试:✅ 正常播放
- 真机预览:✅ 正常播放(需配置域名)
- 音频质量:✅ 清晰流畅
- 用户体验:✅ 加载快速,播放稳定