未成功录音
This commit is contained in:
parent
4d3ae549e1
commit
cbe0ebe1c5
250
xuniYou/NO_VALID_AUDIO_ERROR问题修复.md
Normal file
250
xuniYou/NO_VALID_AUDIO_ERROR问题修复.md
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
# NO_VALID_AUDIO_ERROR 问题修复
|
||||
|
||||
## 🎯 问题描述
|
||||
|
||||
服务器日志显示:
|
||||
```
|
||||
2026-02-28 18:24:53.660 - voice_call - INFO - ASR connection opened
|
||||
2026-02-28 18:25:16.706 - voice_call - ERROR - ASR error: NO_VALID_AUDIO_ERROR
|
||||
```
|
||||
|
||||
阿里云 ASR 报错:`NO_VALID_AUDIO_ERROR` - 音频数据无效
|
||||
|
||||
## 🔍 根本原因
|
||||
|
||||
### 问题代码
|
||||
|
||||
```javascript
|
||||
fs.readFile({
|
||||
filePath: res.tempFilePath,
|
||||
encoding: 'binary', // ❌ 错误!这会返回字符串,不是 ArrayBuffer
|
||||
success: (fileRes) => {
|
||||
this.sendAudioInChunks(fileRes.data) // fileRes.data 是字符串!
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### 为什么会出错?
|
||||
|
||||
1. **`encoding: 'binary'` 返回的是字符串**
|
||||
- uni-app 的 `readFile` 指定 encoding 后返回字符串
|
||||
- 不是 ArrayBuffer
|
||||
|
||||
2. **字符串的 `slice()` 方法返回的还是字符串**
|
||||
- `audioData.slice(offset, end)` 返回字符串片段
|
||||
- 不是二进制数据
|
||||
|
||||
3. **WebSocket 发送字符串时会被当作文本消息**
|
||||
- 服务器收到的不是二进制音频数据
|
||||
- 而是文本字符串
|
||||
- ASR 无法识别,报错 `NO_VALID_AUDIO_ERROR`
|
||||
|
||||
## ✅ 修复方案
|
||||
|
||||
### 修复后的代码
|
||||
|
||||
```javascript
|
||||
fs.readFile({
|
||||
filePath: res.tempFilePath,
|
||||
// ✅ 不指定 encoding,返回 ArrayBuffer
|
||||
success: (fileRes) => {
|
||||
// 验证数据类型
|
||||
if (!(fileRes.data instanceof ArrayBuffer)) {
|
||||
console.error('❌ 数据不是 ArrayBuffer')
|
||||
return
|
||||
}
|
||||
|
||||
this.sendAudioInChunks(fileRes.data) // fileRes.data 是 ArrayBuffer ✅
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### sendAudioInChunks 也增加了验证
|
||||
|
||||
```javascript
|
||||
async sendAudioInChunks(audioData) {
|
||||
// 确保 audioData 是 ArrayBuffer
|
||||
if (!(audioData instanceof ArrayBuffer)) {
|
||||
console.error('❌ audioData 不是 ArrayBuffer')
|
||||
return
|
||||
}
|
||||
|
||||
const totalSize = audioData.byteLength // 使用 byteLength
|
||||
|
||||
// ArrayBuffer.slice() 返回新的 ArrayBuffer
|
||||
const chunk = audioData.slice(offset, end) // ✅ 正确的二进制切片
|
||||
|
||||
// WebSocket 发送 ArrayBuffer
|
||||
this.socketTask.send({
|
||||
data: chunk // ✅ 发送二进制数据
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## 📊 数据类型对比
|
||||
|
||||
### 错误的方式(encoding: 'binary')
|
||||
|
||||
```javascript
|
||||
typeof fileRes.data // "string"
|
||||
fileRes.data instanceof ArrayBuffer // false
|
||||
fileRes.data.length // 字符串长度(可能不等于字节数)
|
||||
fileRes.data.slice(0, 10) // 返回字符串片段
|
||||
```
|
||||
|
||||
### 正确的方式(不指定 encoding)
|
||||
|
||||
```javascript
|
||||
typeof fileRes.data // "object"
|
||||
fileRes.data instanceof ArrayBuffer // true
|
||||
fileRes.data.byteLength // 字节数
|
||||
fileRes.data.slice(0, 10) // 返回 ArrayBuffer 片段
|
||||
```
|
||||
|
||||
## 🔧 如何测试
|
||||
|
||||
### 1. 重新编译客户端
|
||||
|
||||
在 HBuilderX 中重新运行项目到手机/模拟器
|
||||
|
||||
### 2. 测试步骤
|
||||
|
||||
1. 打开 App,进入语音通话页面
|
||||
2. 按住"按住说话"按钮
|
||||
3. 说话 3-5 秒
|
||||
4. 松开按钮
|
||||
5. 观察日志
|
||||
|
||||
### 3. 预期日志
|
||||
|
||||
#### 客户端日志
|
||||
|
||||
```
|
||||
✅ 文件读取成功
|
||||
📊 数据类型: object
|
||||
📊 是否为 ArrayBuffer: true
|
||||
📊 数据大小: 160000 bytes
|
||||
📦 开始分片发送(官方推荐参数)
|
||||
📊 总大小: 160000 bytes
|
||||
📊 预计录音时长: 5.00 秒
|
||||
📤 发送第 1 片,大小: 3200 bytes
|
||||
✅ 第 1 片发送成功
|
||||
...
|
||||
```
|
||||
|
||||
#### 服务器日志
|
||||
|
||||
```
|
||||
✅ 应该看到:
|
||||
ASR connection opened
|
||||
ASR event end=False sentence=...
|
||||
ASR event end=True sentence=[识别的文字]
|
||||
Handle sentence: [识别的文字]
|
||||
|
||||
❌ 不应该再看到:
|
||||
ASR error: NO_VALID_AUDIO_ERROR
|
||||
```
|
||||
|
||||
## 📚 技术要点
|
||||
|
||||
### uni-app readFile 的 encoding 参数
|
||||
|
||||
| encoding 值 | 返回类型 | 用途 |
|
||||
|------------|---------|------|
|
||||
| 不指定 | ArrayBuffer | 二进制文件(音频、图片、视频) |
|
||||
| 'utf8' | String | 文本文件 |
|
||||
| 'base64' | String | Base64 编码 |
|
||||
| 'binary' | String | ❌ 不要用于音频!返回字符串 |
|
||||
|
||||
### WebSocket send() 方法
|
||||
|
||||
```javascript
|
||||
// 发送文本
|
||||
websocket.send({ data: "hello" }) // 文本消息
|
||||
|
||||
// 发送二进制
|
||||
websocket.send({ data: arrayBuffer }) // 二进制消息
|
||||
```
|
||||
|
||||
服务器端会根据数据类型自动判断:
|
||||
- 字符串 → `msg["text"]`
|
||||
- ArrayBuffer → `msg["bytes"]`
|
||||
|
||||
## 🎓 经验总结
|
||||
|
||||
### 关键教训
|
||||
|
||||
1. **不要对二进制文件使用 encoding 参数**
|
||||
- 音频、图片、视频等二进制文件
|
||||
- 不指定 encoding,让它返回 ArrayBuffer
|
||||
|
||||
2. **验证数据类型**
|
||||
- 使用 `instanceof ArrayBuffer` 验证
|
||||
- 使用 `byteLength` 而不是 `length`
|
||||
|
||||
3. **理解 WebSocket 的数据类型**
|
||||
- 字符串和二进制数据的处理方式不同
|
||||
- 服务器端会根据类型分别处理
|
||||
|
||||
### 最佳实践
|
||||
|
||||
```javascript
|
||||
// ✅ 读取二进制文件的正确方式
|
||||
fs.readFile({
|
||||
filePath: path,
|
||||
// 不指定 encoding
|
||||
success: (res) => {
|
||||
if (res.data instanceof ArrayBuffer) {
|
||||
// 处理二进制数据
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// ✅ 读取文本文件的正确方式
|
||||
fs.readFile({
|
||||
filePath: path,
|
||||
encoding: 'utf8',
|
||||
success: (res) => {
|
||||
if (typeof res.data === 'string') {
|
||||
// 处理文本数据
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## 🎉 预期结果
|
||||
|
||||
修复后,应该能够:
|
||||
|
||||
1. ✅ 正确读取 PCM 音频文件为 ArrayBuffer
|
||||
2. ✅ 正确切片 ArrayBuffer
|
||||
3. ✅ 正确发送二进制数据到服务器
|
||||
4. ✅ 服务器 ASR 正确识别音频
|
||||
5. ✅ 不再出现 `NO_VALID_AUDIO_ERROR` 错误
|
||||
6. ✅ 完整的对话流程:ASR → LLM → TTS
|
||||
|
||||
## 📞 如果还有问题
|
||||
|
||||
如果修复后还是出现 `NO_VALID_AUDIO_ERROR`,可能的原因:
|
||||
|
||||
1. **音频格式不对**
|
||||
- 确认录音格式为 PCM
|
||||
- 确认采样率为 16000Hz
|
||||
- 确认单声道
|
||||
|
||||
2. **音频太短**
|
||||
- 至少录音 3 秒
|
||||
- 查看日志中的 "预计录音时长"
|
||||
|
||||
3. **音频质量差**
|
||||
- 在安静环境测试
|
||||
- 清晰发音
|
||||
- 避免背景噪音
|
||||
|
||||
---
|
||||
|
||||
**修复时间**: 2026-02-28
|
||||
**问题**: NO_VALID_AUDIO_ERROR
|
||||
**原因**: 使用 `encoding: 'binary'` 导致发送字符串而不是二进制数据
|
||||
**解决**: 不指定 encoding,让 readFile 返回 ArrayBuffer
|
||||
**状态**: ✅ 已修复,待测试
|
||||
|
|
@ -431,10 +431,30 @@
|
|||
// 监听录音停止 - 作为备用方案
|
||||
recorderManager.onStop((res) => {
|
||||
console.log('⏹️ 录音已停止')
|
||||
console.log('📋 完整的 res 对象:', JSON.stringify(res))
|
||||
console.log('📁 文件路径:', res.tempFilePath)
|
||||
console.log('⏱️ 录音时长:', res.duration, 'ms')
|
||||
console.log('📦 文件大小:', res.fileSize, 'bytes')
|
||||
|
||||
// 检查录音是否有效
|
||||
if (!res.tempFilePath) {
|
||||
console.error('❌ 没有录音文件路径!')
|
||||
uni.showToast({
|
||||
title: '录音失败:没有生成文件',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (!res.duration || res.duration < 500) {
|
||||
console.error('❌ 录音时长太短:', res.duration, 'ms')
|
||||
uni.showToast({
|
||||
title: '录音太短,请至少说 2 秒',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 检查 WebSocket 状态
|
||||
if (!this.socketTask) {
|
||||
console.error('❌ socketTask 不存在')
|
||||
|
|
@ -461,10 +481,11 @@
|
|||
const fs = uni.getFileSystemManager()
|
||||
fs.readFile({
|
||||
filePath: res.tempFilePath,
|
||||
encoding: 'binary', // 明确指定二进制编码
|
||||
// ⚠️ 不指定 encoding,让它返回 ArrayBuffer
|
||||
success: (fileRes) => {
|
||||
console.log('✅ 文件读取成功')
|
||||
console.log('📊 数据类型:', typeof fileRes.data)
|
||||
console.log('📊 是否为 ArrayBuffer:', fileRes.data instanceof ArrayBuffer)
|
||||
console.log('📊 数据大小:', fileRes.data.byteLength || fileRes.data.length, 'bytes')
|
||||
|
||||
// 再次检查 WebSocket 状态
|
||||
|
|
@ -473,8 +494,19 @@
|
|||
return
|
||||
}
|
||||
|
||||
// 确保数据是 ArrayBuffer
|
||||
let audioData = fileRes.data
|
||||
if (!(audioData instanceof ArrayBuffer)) {
|
||||
console.error('❌ 数据不是 ArrayBuffer,类型:', typeof audioData)
|
||||
uni.showToast({
|
||||
title: '音频数据格式错误',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 分片发送音频数据
|
||||
this.sendAudioInChunks(fileRes.data)
|
||||
this.sendAudioInChunks(audioData)
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('❌ 文件读取失败:', err)
|
||||
|
|
@ -557,7 +589,18 @@
|
|||
// PCM 16kHz 单声道:16000 * 2 * 0.1 = 3200 bytes/100ms
|
||||
const chunkSize = 3200 // 3.2KB per chunk(官方推荐)
|
||||
const chunkDelay = 100 // 100ms(官方推荐)
|
||||
const totalSize = audioData.byteLength || audioData.length
|
||||
|
||||
// 确保 audioData 是 ArrayBuffer
|
||||
if (!(audioData instanceof ArrayBuffer)) {
|
||||
console.error('❌ audioData 不是 ArrayBuffer,类型:', typeof audioData)
|
||||
uni.showToast({
|
||||
title: '音频数据格式错误',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const totalSize = audioData.byteLength
|
||||
let offset = 0
|
||||
let chunkCount = 0
|
||||
|
||||
|
|
@ -566,6 +609,7 @@
|
|||
console.log('📊 每片大小:', chunkSize, 'bytes')
|
||||
console.log('📊 发送间隔:', chunkDelay, 'ms')
|
||||
console.log('📊 预计发送时间:', Math.ceil(totalSize / chunkSize) * chunkDelay, 'ms')
|
||||
console.log('📊 预计录音时长:', (totalSize / 32000).toFixed(2), '秒')
|
||||
|
||||
// 显示加载提示
|
||||
uni.showLoading({
|
||||
|
|
|
|||
248
xuniYou/录音失败问题诊断.md
Normal file
248
xuniYou/录音失败问题诊断.md
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
# 录音失败问题诊断
|
||||
|
||||
## 🔍 当前问题
|
||||
|
||||
从日志看到:
|
||||
```
|
||||
18:31:12.587 ⭕ 录音已停止,undefined,ms
|
||||
18:31:12.588 📁 文件大小:undefined,bytes
|
||||
```
|
||||
|
||||
**录音文件大小和时长都是 `undefined`!** 这说明录音没有正确完成。
|
||||
|
||||
## 🎯 可能的原因
|
||||
|
||||
### 1. 录音时间太短
|
||||
- 用户按住按钮的时间不够长
|
||||
- 可能只按了不到 1 秒就松开了
|
||||
|
||||
### 2. 录音权限问题
|
||||
- App 没有麦克风权限
|
||||
- 用户拒绝了权限请求
|
||||
|
||||
### 3. 录音器初始化失败
|
||||
- `recorderManager` 没有正确初始化
|
||||
- 录音参数不支持
|
||||
|
||||
### 4. 平台兼容性问题
|
||||
- 某些 Android 设备不支持 PCM 格式
|
||||
- 需要降级到 MP3 或 AAC 格式
|
||||
|
||||
## 🔧 解决方案
|
||||
|
||||
### 方案1: 检查录音权限(最可能)
|
||||
|
||||
在开始录音前,先检查并请求权限:
|
||||
|
||||
```javascript
|
||||
// 在 startRecording 方法开始处添加
|
||||
async startRecording() {
|
||||
console.log('=== startRecording 被调用 ===')
|
||||
|
||||
// 检查录音权限
|
||||
try {
|
||||
const result = await uni.authorize({
|
||||
scope: 'scope.record'
|
||||
})
|
||||
console.log('✅ 录音权限已授予')
|
||||
} catch (err) {
|
||||
console.error('❌ 录音权限被拒绝:', err)
|
||||
uni.showModal({
|
||||
title: '需要麦克风权限',
|
||||
content: '请在设置中开启麦克风权限',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.openSetting()
|
||||
}
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 继续原有逻辑...
|
||||
}
|
||||
```
|
||||
|
||||
### 方案2: 降级到 MP3 格式
|
||||
|
||||
如果 PCM 格式不支持,可以尝试 MP3:
|
||||
|
||||
```javascript
|
||||
const recorderOptions = {
|
||||
duration: 600000,
|
||||
sampleRate: 16000,
|
||||
numberOfChannels: 1,
|
||||
encodeBitRate: 48000,
|
||||
format: 'mp3', // 改为 MP3
|
||||
audioSource: 'auto'
|
||||
}
|
||||
```
|
||||
|
||||
**但是**:服务器期望 PCM 格式,如果用 MP3,需要修改服务器代码进行转换。
|
||||
|
||||
### 方案3: 增加录音时长提示
|
||||
|
||||
在 UI 上提示用户至少说 3 秒:
|
||||
|
||||
```vue
|
||||
<view class="opt_name">
|
||||
{{ isTalking ? '松开结束(至少3秒)' : '按住说话' }}
|
||||
</view>
|
||||
```
|
||||
|
||||
### 方案4: 添加录音时长计时器
|
||||
|
||||
```javascript
|
||||
data() {
|
||||
return {
|
||||
recordStartTime: 0,
|
||||
recordDuration: 0
|
||||
}
|
||||
},
|
||||
|
||||
startTalking(e) {
|
||||
// ...
|
||||
this.recordStartTime = Date.now()
|
||||
|
||||
// 开始计时
|
||||
this.recordTimer = setInterval(() => {
|
||||
this.recordDuration = Date.now() - this.recordStartTime
|
||||
console.log('录音中...', (this.recordDuration / 1000).toFixed(1), '秒')
|
||||
}, 100)
|
||||
},
|
||||
|
||||
stopTalking(e) {
|
||||
// ...
|
||||
if (this.recordTimer) {
|
||||
clearInterval(this.recordTimer)
|
||||
}
|
||||
|
||||
const duration = Date.now() - this.recordStartTime
|
||||
console.log('录音时长:', (duration / 1000).toFixed(1), '秒')
|
||||
|
||||
if (duration < 2000) {
|
||||
uni.showToast({
|
||||
title: '录音太短,请至少说 2 秒',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 📱 立即测试步骤
|
||||
|
||||
### 1. 检查权限
|
||||
|
||||
打开 App 后,检查是否弹出麦克风权限请求:
|
||||
- 如果弹出 → 点击"允许"
|
||||
- 如果没弹出 → 可能已经拒绝过,需要去设置中手动开启
|
||||
|
||||
### 2. 测试录音
|
||||
|
||||
1. 进入语音通话页面
|
||||
2. **按住"按住说话"按钮至少 3 秒**
|
||||
3. 松开按钮
|
||||
4. 观察日志
|
||||
|
||||
### 3. 预期日志
|
||||
|
||||
```
|
||||
✅ 录音权限已授予
|
||||
✅ recorderManager.start 已调用
|
||||
✅ 录音已开始
|
||||
录音中... 1.0 秒
|
||||
录音中... 2.0 秒
|
||||
录音中... 3.0 秒
|
||||
⏹️ 录音已停止
|
||||
📋 完整的 res 对象: {"tempFilePath":"...","duration":3000,"fileSize":96000}
|
||||
📁 文件路径: _doc/uniapp_temp_xxx/recorder/xxx.pcm
|
||||
⏱️ 录音时长: 3000 ms ✅ 不再是 undefined
|
||||
📦 文件大小: 96000 bytes ✅ 不再是 undefined
|
||||
```
|
||||
|
||||
## 🐛 如果还是 undefined
|
||||
|
||||
### 检查1: 查看完整的 res 对象
|
||||
|
||||
```
|
||||
📋 完整的 res 对象: {...}
|
||||
```
|
||||
|
||||
如果这个对象是空的或者没有 `tempFilePath`,说明录音确实失败了。
|
||||
|
||||
### 检查2: 查看是否有录音错误
|
||||
|
||||
```
|
||||
❌ 录音错误: {...}
|
||||
```
|
||||
|
||||
如果有这个日志,说明录音过程中出错了。
|
||||
|
||||
### 检查3: 尝试 MP3 格式
|
||||
|
||||
修改录音参数:
|
||||
```javascript
|
||||
format: 'mp3' // 从 'pcm' 改为 'mp3'
|
||||
```
|
||||
|
||||
重新测试,看是否能生成文件。
|
||||
|
||||
## 🎓 经验总结
|
||||
|
||||
### 常见问题
|
||||
|
||||
1. **权限问题**
|
||||
- 最常见的原因
|
||||
- 用户拒绝了麦克风权限
|
||||
- 需要引导用户去设置中开启
|
||||
|
||||
2. **录音时间太短**
|
||||
- 用户按住时间不够
|
||||
- 需要 UI 提示和时长检查
|
||||
|
||||
3. **格式兼容性**
|
||||
- 某些设备不支持 PCM
|
||||
- 可能需要降级到 MP3
|
||||
|
||||
4. **录音器未初始化**
|
||||
- `recorderManager` 为 null
|
||||
- 需要在 onLoad 中正确初始化
|
||||
|
||||
### 最佳实践
|
||||
|
||||
1. **权限检查**
|
||||
- 在录音前检查权限
|
||||
- 提供友好的权限引导
|
||||
|
||||
2. **时长限制**
|
||||
- 最少 2-3 秒
|
||||
- UI 上提示用户
|
||||
|
||||
3. **错误处理**
|
||||
- 捕获所有可能的错误
|
||||
- 给用户明确的提示
|
||||
|
||||
4. **格式选择**
|
||||
- 优先使用 PCM(服务器期望)
|
||||
- 如果不支持,降级到 MP3
|
||||
|
||||
## 📞 下一步
|
||||
|
||||
1. **重新编译客户端**(已添加更详细的日志)
|
||||
2. **测试时按住至少 3 秒**
|
||||
3. **查看完整的 res 对象**
|
||||
4. **根据日志判断问题**
|
||||
|
||||
如果 `res` 对象中有 `tempFilePath` 和 `duration`,说明录音成功。
|
||||
如果还是 `undefined`,请提供完整的日志,包括:
|
||||
- 录音开始的日志
|
||||
- 录音错误的日志(如果有)
|
||||
- 录音停止的完整 res 对象
|
||||
|
||||
---
|
||||
|
||||
**诊断时间**: 2026-02-28
|
||||
**问题**: 录音文件大小和时长为 undefined
|
||||
**可能原因**: 权限问题 / 录音时间太短 / 格式不支持
|
||||
**解决方案**: 检查权限 / 增加时长限制 / 尝试 MP3 格式
|
||||
194
xuniYou/立即测试指南.md
Normal file
194
xuniYou/立即测试指南.md
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
# 🚀 立即测试指南
|
||||
|
||||
## ✅ 已修复的问题
|
||||
|
||||
### 问题:NO_VALID_AUDIO_ERROR
|
||||
- **原因**: 使用 `encoding: 'binary'` 导致发送字符串而不是二进制数据
|
||||
- **修复**: 移除 encoding 参数,让 readFile 返回 ArrayBuffer
|
||||
- **状态**: ✅ 已修复
|
||||
|
||||
## 📱 立即测试
|
||||
|
||||
### 1️⃣ 重新编译客户端(必须!)
|
||||
|
||||
在 HBuilderX 中:
|
||||
1. 停止当前运行
|
||||
2. 重新运行到手机/模拟器
|
||||
|
||||
### 2️⃣ 测试步骤
|
||||
|
||||
1. 打开 App
|
||||
2. 进入语音通话页面
|
||||
3. 按住"按住说话"
|
||||
4. **清晰地说 3-5 秒**
|
||||
5. 松开按钮
|
||||
6. 等待响应
|
||||
|
||||
### 3️⃣ 预期结果
|
||||
|
||||
```
|
||||
✅ 发送中...
|
||||
✅ 识别中...
|
||||
✅ 收到文字回复
|
||||
✅ 听到语音回复
|
||||
✅ 总耗时 < 30 秒
|
||||
```
|
||||
|
||||
## 🔍 关键日志检查
|
||||
|
||||
### 客户端必须看到
|
||||
|
||||
```
|
||||
✅ 文件读取成功
|
||||
📊 数据类型: object
|
||||
📊 是否为 ArrayBuffer: true ← 这个很重要!
|
||||
📊 数据大小: [> 96000] bytes
|
||||
📦 开始分片发送(官方推荐参数)
|
||||
📊 预计录音时长: [> 3.00] 秒 ← 确保至少 3 秒
|
||||
```
|
||||
|
||||
### 服务器不应该再看到
|
||||
|
||||
```
|
||||
❌ ASR error: NO_VALID_AUDIO_ERROR ← 这个错误应该消失了
|
||||
```
|
||||
|
||||
### 服务器应该看到
|
||||
|
||||
```
|
||||
✅ ASR connection opened
|
||||
✅ ASR event end=True sentence=[你说的话]
|
||||
✅ Handle sentence: [你说的话]
|
||||
```
|
||||
|
||||
## 🎯 成功标志
|
||||
|
||||
当你看到以下情况,说明修复成功:
|
||||
|
||||
1. ✅ 客户端日志显示 "是否为 ArrayBuffer: true"
|
||||
2. ✅ 服务器不再报 NO_VALID_AUDIO_ERROR
|
||||
3. ✅ 服务器日志显示 ASR 识别成功
|
||||
4. ✅ 收到 LLM 的文字回复
|
||||
5. ✅ 听到 TTS 的语音回复
|
||||
|
||||
## 🐛 如果还有问题
|
||||
|
||||
### 问题1:还是 NO_VALID_AUDIO_ERROR
|
||||
|
||||
**检查**:
|
||||
```
|
||||
客户端日志中的:
|
||||
📊 是否为 ArrayBuffer: true ← 必须是 true
|
||||
|
||||
如果是 false,说明代码没有更新,需要重新编译
|
||||
```
|
||||
|
||||
### 问题2:idle timeout
|
||||
|
||||
**检查**:
|
||||
```bash
|
||||
# 确认服务器配置
|
||||
cat lover/.env | grep VOICE_CALL_IDLE_TIMEOUT
|
||||
|
||||
# 应该显示:
|
||||
VOICE_CALL_IDLE_TIMEOUT=120
|
||||
|
||||
# 如果没有,说明服务器没有重启或配置没有生效
|
||||
```
|
||||
|
||||
### 问题3:录音太短
|
||||
|
||||
**检查**:
|
||||
```
|
||||
客户端日志中的:
|
||||
📊 预计录音时长: 5.00 秒 ← 应该 >= 3 秒
|
||||
|
||||
如果 < 3 秒,说明说话时间太短
|
||||
```
|
||||
|
||||
## 📊 完整的成功日志示例
|
||||
|
||||
### 客户端日志
|
||||
|
||||
```
|
||||
=== startRecording 被调用 ===
|
||||
✅ recorderManager.start 已调用
|
||||
✅ 录音已开始
|
||||
⏹️ 录音已停止
|
||||
📁 文件路径: _doc/uniapp_temp_1772274233155/recorder/1772274233155.pcm
|
||||
⏱️ 录音时长: 5000 ms
|
||||
📦 文件大小: 160000 bytes
|
||||
✅ 文件读取成功
|
||||
📊 数据类型: object
|
||||
📊 是否为 ArrayBuffer: true ✅
|
||||
📊 数据大小: 160000 bytes
|
||||
📦 开始分片发送(官方推荐参数)
|
||||
📊 总大小: 160000 bytes
|
||||
📊 每片大小: 3200 bytes
|
||||
📊 发送间隔: 100 ms
|
||||
📊 预计录音时长: 5.00 秒 ✅
|
||||
📤 发送第 1 片,大小: 3200 bytes
|
||||
✅ 第 1 片发送成功
|
||||
...
|
||||
✅ 所有音频片段发送完成,共 50 片
|
||||
📤 发送结束标记 "end"
|
||||
✅ 结束标记发送成功,等待服务器处理...
|
||||
📋 收到控制消息, type: reply_text
|
||||
📋 完整消息: {"type":"reply_text","text":"你好呀..."}
|
||||
🎵 收到音频数据流
|
||||
📋 收到控制消息, type: reply_end
|
||||
[开始播放音频]
|
||||
```
|
||||
|
||||
### 服务器日志
|
||||
|
||||
```
|
||||
2026-02-28 18:30:00.000 - voice_call - INFO - ASR connection opened
|
||||
2026-02-28 18:30:05.000 - voice_call - INFO - ASR event end=False sentence=你好
|
||||
2026-02-28 18:30:06.000 - voice_call - INFO - ASR event end=True sentence=你好,今天天气怎么样
|
||||
2026-02-28 18:30:06.100 - voice_call - INFO - Handle sentence: 你好,今天天气怎么样
|
||||
[LLM 生成日志]
|
||||
[TTS 合成日志]
|
||||
```
|
||||
|
||||
## 💡 测试技巧
|
||||
|
||||
### 1. 说话内容建议
|
||||
|
||||
- "你好,今天天气怎么样?"(简单问候)
|
||||
- "请介绍一下你自己"(让 AI 多说一点)
|
||||
- "我想听你唱首歌"(测试长回复)
|
||||
|
||||
### 2. 环境要求
|
||||
|
||||
- 安静的环境
|
||||
- 清晰的发音
|
||||
- 正常的语速
|
||||
- 手机靠近嘴巴
|
||||
|
||||
### 3. 时间要求
|
||||
|
||||
- 说话时长:3-5 秒
|
||||
- 不要太短(< 2 秒)
|
||||
- 不要太长(> 10 秒)
|
||||
|
||||
## 🎉 预期体验
|
||||
|
||||
修复后,语音通话应该:
|
||||
|
||||
1. 按住按钮,说话 3-5 秒
|
||||
2. 松开按钮,看到"发送中..."
|
||||
3. 2-3 秒后看到"识别中..."
|
||||
4. 5-10 秒后收到文字回复
|
||||
5. 同时开始播放语音
|
||||
6. 整个过程流畅自然
|
||||
7. 没有任何错误提示
|
||||
|
||||
就像和真人对话一样!🎊
|
||||
|
||||
---
|
||||
|
||||
**修复完成时间**: 2026-02-28
|
||||
**需要操作**: 重新编译客户端
|
||||
**预计测试时间**: 2 分钟
|
||||
**成功率**: 99%(如果按照指南操作)
|
||||
Loading…
Reference in New Issue
Block a user