287 lines
6.5 KiB
Markdown
287 lines
6.5 KiB
Markdown
|
|
# 语音播放问题排查指南
|
|||
|
|
|
|||
|
|
## 🔍 问题现象
|
|||
|
|
|
|||
|
|
后端成功生成了 AI 语音(120463 字节),但前端没有播放出来。
|
|||
|
|
|
|||
|
|
## 📋 排查步骤
|
|||
|
|
|
|||
|
|
### 1. 检查前端是否收到音频数据
|
|||
|
|
|
|||
|
|
重新编译并运行,查看前端控制台日志:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
✅ 对话响应: ...
|
|||
|
|
✅ 识别结果: ghost~来啦?
|
|||
|
|
✅ AI回复: ghost~来啦?刚在有《夏日友人帐》...
|
|||
|
|
✅ 音频数据: XXXXX 字符 <-- 应该看到这行
|
|||
|
|
🔊 开始播放 AI 语音回复...
|
|||
|
|
🔊 音频数据前100字符: ...
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**如果没有看到"音频数据"**:
|
|||
|
|
- 问题在后端返回数据格式
|
|||
|
|
- 检查后端响应结构
|
|||
|
|
|
|||
|
|
**如果看到了"音频数据"**:
|
|||
|
|
- 继续下一步
|
|||
|
|
|
|||
|
|
### 2. 检查 playAIVoice 是否被调用
|
|||
|
|
|
|||
|
|
查看日志:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
🔊 playAIVoice 被调用
|
|||
|
|
🔊 base64Audio 长度: XXXXX
|
|||
|
|
🔊 aiText: ...
|
|||
|
|
📦 开始解码 base64...
|
|||
|
|
✅ 音频数据解码完成,大小: XXXXX bytes
|
|||
|
|
📱 APP 环境,保存并播放音频
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**如果没有看到这些日志**:
|
|||
|
|
- `playAIVoice` 没有被调用
|
|||
|
|
- 检查 `audioData` 是否为空
|
|||
|
|
|
|||
|
|
**如果看到了这些日志**:
|
|||
|
|
- 继续下一步
|
|||
|
|
|
|||
|
|
### 3. 检查文件保存是否成功
|
|||
|
|
|
|||
|
|
查看日志:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
✅ 获取 _doc 目录成功
|
|||
|
|
✅ 创建文件成功: ai_voice_xxx.mp3
|
|||
|
|
✅ 文件写入成功
|
|||
|
|
📁 文件路径: /storage/emulated/0/Android/data/...
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**如果看到错误**:
|
|||
|
|
- 文件系统权限问题
|
|||
|
|
- 检查 APP 存储权限
|
|||
|
|
|
|||
|
|
**如果文件保存成功**:
|
|||
|
|
- 继续下一步
|
|||
|
|
|
|||
|
|
### 4. 检查音频播放
|
|||
|
|
|
|||
|
|
查看日志:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
🎵 创建音频上下文...
|
|||
|
|
🎵 设置音频源: /storage/emulated/0/Android/data/.../ai_voice_xxx.mp3
|
|||
|
|
🔊 AI 语音开始播放 <-- 应该看到这行
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**如果看到播放错误**:
|
|||
|
|
- 音频格式问题
|
|||
|
|
- 音频文件损坏
|
|||
|
|
- 播放器不支持
|
|||
|
|
|
|||
|
|
## 🔧 常见问题和解决方案
|
|||
|
|
|
|||
|
|
### 问题 1:没有收到音频数据
|
|||
|
|
|
|||
|
|
**症状**:
|
|||
|
|
```
|
|||
|
|
✅ AI回复: ...
|
|||
|
|
⚠️ 没有收到音频数据
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**原因**:
|
|||
|
|
- 后端返回数据格式不正确
|
|||
|
|
- `audio_data` 字段缺失
|
|||
|
|
|
|||
|
|
**解决方案**:
|
|||
|
|
|
|||
|
|
检查后端响应:
|
|||
|
|
```bash
|
|||
|
|
# 使用 curl 测试
|
|||
|
|
curl -X POST http://127.0.0.1:30101/voice/call/conversation \
|
|||
|
|
-H "Content-Type: application/json" \
|
|||
|
|
-H "Authorization: Bearer YOUR_TOKEN" \
|
|||
|
|
-d '{"audio_data":"...","format":"wav"}' \
|
|||
|
|
| jq '.data.audio_data' | head -c 100
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
应该看到 base64 字符串。
|
|||
|
|
|
|||
|
|
### 问题 2:base64 解码失败
|
|||
|
|
|
|||
|
|
**症状**:
|
|||
|
|
```
|
|||
|
|
❌ 播放 AI 语音失败
|
|||
|
|
错误类型: InvalidCharacterError
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**原因**:
|
|||
|
|
- base64 字符串包含非法字符
|
|||
|
|
- 字符串被截断
|
|||
|
|
|
|||
|
|
**解决方案**:
|
|||
|
|
|
|||
|
|
检查 base64 字符串:
|
|||
|
|
```javascript
|
|||
|
|
console.log('base64 前10字符:', audioData.substring(0, 10))
|
|||
|
|
console.log('base64 后10字符:', audioData.substring(audioData.length - 10))
|
|||
|
|
console.log('是否包含非法字符:', /[^A-Za-z0-9+/=]/.test(audioData))
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 问题 3:文件保存失败
|
|||
|
|
|
|||
|
|
**症状**:
|
|||
|
|
```
|
|||
|
|
❌ 获取文件系统失败
|
|||
|
|
❌ 创建文件失败
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**原因**:
|
|||
|
|
- APP 没有存储权限
|
|||
|
|
- 文件路径不存在
|
|||
|
|
|
|||
|
|
**解决方案**:
|
|||
|
|
|
|||
|
|
1. 检查 APP 权限:
|
|||
|
|
```javascript
|
|||
|
|
// 在 manifest.json 中添加
|
|||
|
|
"permissions": {
|
|||
|
|
"android.permission.WRITE_EXTERNAL_STORAGE": {},
|
|||
|
|
"android.permission.READ_EXTERNAL_STORAGE": {}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
2. 使用其他目录:
|
|||
|
|
```javascript
|
|||
|
|
// 尝试使用 _downloads 目录
|
|||
|
|
plus.io.resolveLocalFileSystemURL('_downloads/', ...)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 问题 4:音频播放失败
|
|||
|
|
|
|||
|
|
**症状**:
|
|||
|
|
```
|
|||
|
|
✅ 文件写入成功
|
|||
|
|
🎵 设置音频源: ...
|
|||
|
|
❌ 播放失败: {errMsg: "..."}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**原因**:
|
|||
|
|
- 音频格式不支持
|
|||
|
|
- 文件路径错误
|
|||
|
|
- 音频文件损坏
|
|||
|
|
|
|||
|
|
**解决方案**:
|
|||
|
|
|
|||
|
|
1. **验证音频文件**:
|
|||
|
|
```javascript
|
|||
|
|
// 检查文件是否存在
|
|||
|
|
plus.io.resolveLocalFileSystemURL(filePath, (entry) => {
|
|||
|
|
entry.file((file) => {
|
|||
|
|
console.log('文件大小:', file.size)
|
|||
|
|
console.log('文件类型:', file.type)
|
|||
|
|
})
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
2. **使用绝对路径**:
|
|||
|
|
```javascript
|
|||
|
|
// 使用 fileEntry.toLocalURL()
|
|||
|
|
const audioUrl = fileEntry.toLocalURL()
|
|||
|
|
audioContext.src = audioUrl
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
3. **测试音频文件**:
|
|||
|
|
```javascript
|
|||
|
|
// 保存到相册,手动播放测试
|
|||
|
|
plus.gallery.save(filePath, () => {
|
|||
|
|
console.log('已保存到相册,请手动播放测试')
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 🎯 调试技巧
|
|||
|
|
|
|||
|
|
### 1. 保存音频文件到相册
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// 在 playAIVoice 中添加
|
|||
|
|
writer.onwrite = () => {
|
|||
|
|
console.log('✅ 文件写入成功')
|
|||
|
|
|
|||
|
|
// 保存到相册以便测试
|
|||
|
|
plus.gallery.save(fileEntry.fullPath, () => {
|
|||
|
|
console.log('✅ 已保存到相册')
|
|||
|
|
uni.showToast({
|
|||
|
|
title: '音频已保存到相册',
|
|||
|
|
icon: 'none'
|
|||
|
|
})
|
|||
|
|
}, (error) => {
|
|||
|
|
console.error('保存到相册失败:', error)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 继续播放...
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 使用系统播放器测试
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// 使用系统播放器打开
|
|||
|
|
plus.runtime.openFile(fileEntry.fullPath, {}, (error) => {
|
|||
|
|
console.error('打开文件失败:', error)
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. 对比文件大小
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
console.log('原始 base64 长度:', base64Audio.length)
|
|||
|
|
console.log('解码后字节数:', bytes.length)
|
|||
|
|
console.log('预期字节数:', Math.ceil(base64Audio.length * 3 / 4))
|
|||
|
|
console.log('后端返回大小:', 120463) // 从后端日志获取
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
应该接近 120463 字节。
|
|||
|
|
|
|||
|
|
## 📝 完整的调试日志示例
|
|||
|
|
|
|||
|
|
**成功的日志**:
|
|||
|
|
```
|
|||
|
|
📤 发送语音对话请求...
|
|||
|
|
✅ 对话响应: {statusCode: 200, data: {...}}
|
|||
|
|
✅ 识别结果: ghost~来啦?
|
|||
|
|
✅ AI回复: ghost~来啦?刚在有《夏日友人帐》...
|
|||
|
|
✅ 音频数据: 160616 字符
|
|||
|
|
🔊 开始播放 AI 语音回复...
|
|||
|
|
🔊 音频数据前100字符: SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjYxLjEuMTAwAAAAAAAAAAAAAAD/+5DEAAAAAAAAAAAAAAAAAAAAAABJbmZvAAAADwAAAA...
|
|||
|
|
🔊 playAIVoice 被调用
|
|||
|
|
🔊 base64Audio 长度: 160616
|
|||
|
|
🔊 aiText: ghost~来啦?刚在有《夏日友人帐》...
|
|||
|
|
📦 开始解码 base64...
|
|||
|
|
✅ 音频数据解码完成,大小: 120463 bytes
|
|||
|
|
📱 APP 环境,保存并播放音频
|
|||
|
|
✅ 获取 _doc 目录成功
|
|||
|
|
✅ 创建文件成功: ai_voice_1709636448745.mp3
|
|||
|
|
📝 开始写入文件...
|
|||
|
|
✅ 文件写入成功
|
|||
|
|
📁 文件路径: /storage/emulated/0/Android/data/io.dcloud.HBuilder/apps/HBuilder/doc/ai_voice_1709636448745.mp3
|
|||
|
|
🎵 创建音频上下文...
|
|||
|
|
🎵 设置音频源: /storage/emulated/0/Android/data/io.dcloud.HBuilder/apps/HBuilder/doc/ai_voice_1709636448745.mp3
|
|||
|
|
🔊 AI 语音开始播放
|
|||
|
|
✅ AI 语音播放完成
|
|||
|
|
🗑️ 临时文件已清理
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 🚀 下一步
|
|||
|
|
|
|||
|
|
如果以上步骤都无法解决问题,请提供:
|
|||
|
|
|
|||
|
|
1. 完整的前端控制台日志
|
|||
|
|
2. 后端日志(TTS 合成部分)
|
|||
|
|
3. 设备信息(Android 版本、APP 版本)
|
|||
|
|
4. 是否有其他音频播放功能正常工作
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**最后更新**: 2026-03-05
|