Ai_GirlFriend/xuniYou/最终修复说明.md
2026-03-02 18:57:11 +08:00

260 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# NO_VALID_AUDIO_ERROR 最终修复说明
## 🎯 问题根源
经过详细分析日志,发现问题的根本原因是:
1.`ptt_on` 信号已发送并被服务器接收(日志显示 `ptt_enabled`
2. ✅ 录音已启动
3.**但是 `onStop` 回调没有被触发**
4. ❌ 导致录音停止后,没有读取和发送音频文件
5. ❌ 服务器等待音频数据超时,报错 `NO_VALID_AUDIO_ERROR`
## 🔧 最终修复方案
### 修复内容
将录音监听器的设置从 `startRecording` 方法中移出,改为在初始化时设置一次。
**原因:**
- 每次调用 `startRecording` 都会重新设置监听器
- 这可能导致监听器被覆盖或 `this` 上下文丢失
- 导致 `onStop` 回调无法正常触发
### 代码变更
#### 1. 在 `onLoad` 中调用 `setupRecorderListeners()`
```javascript
onLoad() {
// 初始化 recorderManager
recorderManager = uni.getRecorderManager()
// 设置录音监听器(只设置一次)
this.setupRecorderListeners()
this.getCallDuration()
this.initAudio()
}
```
#### 2. 新增 `setupRecorderListeners()` 方法
```javascript
setupRecorderListeners() {
// 监听录音开始
recorderManager.onStart(() => {
console.log('✅ 录音已开始')
})
// 监听录音错误
recorderManager.onError((err) => {
console.error('❌ 录音错误:', err)
this.isRecording = false
})
// 监听录音停止 - 读取文件并发送
recorderManager.onStop((res) => {
console.log('⏹️ 录音已停止')
// ... 读取文件并发送的逻辑
})
// 监听音频帧 - 实时发送(如果支持)
recorderManager.onFrameRecorded((res) => {
// ... 实时发送音频帧的逻辑
})
}
```
#### 3. 简化 `startRecording()` 方法
```javascript
async startRecording() {
if (this.isRecording) return
this.isRecording = true
// 直接启动录音,不再设置监听器
const recorderOptions = {
duration: 600000,
sampleRate: 16000,
numberOfChannels: 1,
encodeBitRate: 48000,
format: 'pcm',
frameSize: 5, // 启用实时音频帧
audioSource: 'auto'
}
recorderManager.start(recorderOptions)
}
```
## 📊 预期效果
修复后的完整流程:
```
1. 用户按住"按住说话"
2. 发送 ptt_on 信号 ✅
3. 服务器响应 ptt_enabled ✅
4. 启动录音 ✅
5. 实时发送音频帧(如果支持)
用户松开按钮
6. 停止录音,触发 onStop 回调 ← 修复重点
7. 读取录音文件ArrayBuffer
8. 分片发送音频数据
9. 发送 ptt_off 信号
10. 服务器 ASR 识别 → LLM 生成 → TTS 合成
```
## 🧪 测试步骤
### 1. 重新编译
在 HBuilderX 中:
1. 停止当前运行
2. 清理缓存(菜单 -> 运行 -> 清理缓存)
3. 重新运行到手机/模拟器
### 2. 测试并观察日志
按住说话 3-5 秒,观察日志:
**预期日志(成功):**
```
✅ 开始说话, isTalking 设置为: true
📤 发送 ptt_on 信号
✅ ptt_on 信号发送成功
📋 收到服务器消息: {"type":"info","msg":"ptt_enabled"} ← 服务器确认
录音未启动,开始启动录音
=== startRecording 被调用 ===
🎙️ 启动 recorderManager
✅ 录音已开始
[用户松开按钮]
=== stopTalking 被调用 ===
🛑 停止录音并准备发送...
⏹️ 录音已停止 ← 关键!这个日志必须出现
📁 文件路径: /xxx/recorder/xxx.pcm
✅ 文件读取成功
📊 是否为 ArrayBuffer: true
📊 文件大小: 160000 bytes
📦 开始分片发送
📤 发送第 1 片,大小: 3200 bytes
✅ 第 1 片发送成功
...
✅ 所有音频片段发送完成
✅ ptt_off 信号发送成功
```
### 3. 关键检查点
- ✅ 必须看到 "⏹️ 录音已停止" 日志
- ✅ 必须看到 "✅ 文件读取成功" 日志
- ✅ 必须看到 "📤 发送第 X 片" 日志
- ✅ 服务器不再报 `NO_VALID_AUDIO_ERROR`
## 🐛 如果还有问题
### 问题 1还是没有 "⏹️ 录音已停止" 日志
**可能原因:**
- 录音时间太短(< 500ms
- 录音权限未授予
- 设备不支持 PCM 格式
**解决方案:**
1. 确保录音至少 2-3
2. 检查 App 权限设置确保麦克风权限已授予
3. 尝试修改录音格式为 `aac`但需要服务器支持
### 问题 2有 "⏹️ 录音已停止" 但没有文件路径
**可能原因:**
- 录音失败没有生成文件
- 存储权限问题
**解决方案:**
1. 检查日志中的录音错误信息
2. 确保 App 有存储权限
3. 检查设备存储空间是否充足
### 问题 3文件读取失败
**可能原因:**
- 文件路径无效
- 文件系统权限问题
**解决方案:**
1. 检查 `res.tempFilePath` 的值
2. 尝试使用绝对路径
3. 检查文件是否真实存在
## 📝 技术要点总结
### 1. 监听器设置的最佳实践
**错误做法:**
```javascript
startRecording() {
// 每次都设置监听器
recorderManager.onStop(() => { ... })
recorderManager.start()
}
```
**正确做法:**
```javascript
onLoad() {
// 初始化时设置一次
this.setupRecorderListeners()
}
startRecording() {
// 只启动录音
recorderManager.start()
}
```
### 2. this 上下文问题
在回调函数中使用箭头函数确保 `this` 指向组件实例
```javascript
recorderManager.onStop((res) => {
// 箭头函数this 指向组件
this.socketTask.send(...)
})
```
### 3. 双重保障机制
- **主方案** `onFrameRecorded` 实时发送音频帧低延迟
- **备用方案** `onStop` 发送完整文件兼容性好
大多数设备会使用备用方案因为 `onFrameRecorded` 支持有限
## ✅ 修复完成
所有代码修改已完成请重新编译测试
---
**修复时间:** 2026-03-02
**问题:** NO_VALID_AUDIO_ERROR
**根本原因:** onStop 回调未触发监听器设置位置不当
**解决方案:** 将监听器设置移到初始化阶段只设置一次
**状态:** 已修复待测试