6.1 KiB
6.1 KiB
NO_VALID_AUDIO_ERROR 最终修复说明
🎯 问题根源
经过详细分析日志,发现问题的根本原因是:
- ✅
ptt_on信号已发送并被服务器接收(日志显示ptt_enabled) - ✅ 录音已启动
- ❌ 但是
onStop回调没有被触发 - ❌ 导致录音停止后,没有读取和发送音频文件
- ❌ 服务器等待音频数据超时,报错
NO_VALID_AUDIO_ERROR
🔧 最终修复方案
修复内容
将录音监听器的设置从 startRecording 方法中移出,改为在初始化时设置一次。
原因:
- 每次调用
startRecording都会重新设置监听器 - 这可能导致监听器被覆盖或
this上下文丢失 - 导致
onStop回调无法正常触发
代码变更
1. 在 onLoad 中调用 setupRecorderListeners()
onLoad() {
// 初始化 recorderManager
recorderManager = uni.getRecorderManager()
// 设置录音监听器(只设置一次)
this.setupRecorderListeners()
this.getCallDuration()
this.initAudio()
}
2. 新增 setupRecorderListeners() 方法
setupRecorderListeners() {
// 监听录音开始
recorderManager.onStart(() => {
console.log('✅ 录音已开始')
})
// 监听录音错误
recorderManager.onError((err) => {
console.error('❌ 录音错误:', err)
this.isRecording = false
})
// 监听录音停止 - 读取文件并发送
recorderManager.onStop((res) => {
console.log('⏹️ 录音已停止')
// ... 读取文件并发送的逻辑
})
// 监听音频帧 - 实时发送(如果支持)
recorderManager.onFrameRecorded((res) => {
// ... 实时发送音频帧的逻辑
})
}
3. 简化 startRecording() 方法
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 中:
- 停止当前运行
- 清理缓存(菜单 -> 运行 -> 清理缓存)
- 重新运行到手机/模拟器
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 格式
解决方案:
- 确保录音至少 2-3 秒
- 检查 App 权限设置,确保麦克风权限已授予
- 尝试修改录音格式为
aac(但需要服务器支持)
问题 2:有 "⏹️ 录音已停止" 但没有文件路径
可能原因:
- 录音失败,没有生成文件
- 存储权限问题
解决方案:
- 检查日志中的录音错误信息
- 确保 App 有存储权限
- 检查设备存储空间是否充足
问题 3:文件读取失败
可能原因:
- 文件路径无效
- 文件系统权限问题
解决方案:
- 检查
res.tempFilePath的值 - 尝试使用绝对路径
- 检查文件是否真实存在
📝 技术要点总结
1. 监听器设置的最佳实践
❌ 错误做法:
startRecording() {
// 每次都设置监听器
recorderManager.onStop(() => { ... })
recorderManager.start()
}
✅ 正确做法:
onLoad() {
// 初始化时设置一次
this.setupRecorderListeners()
}
startRecording() {
// 只启动录音
recorderManager.start()
}
2. this 上下文问题
在回调函数中使用箭头函数确保 this 指向组件实例:
recorderManager.onStop((res) => {
// 箭头函数,this 指向组件
this.socketTask.send(...)
})
3. 双重保障机制
- 主方案:
onFrameRecorded实时发送音频帧(低延迟) - 备用方案:
onStop发送完整文件(兼容性好)
大多数设备会使用备用方案,因为 onFrameRecorded 支持有限。
✅ 修复完成
所有代码修改已完成,请重新编译测试!
修复时间: 2026-03-02
问题: NO_VALID_AUDIO_ERROR
根本原因: onStop 回调未触发,监听器设置位置不当
解决方案: 将监听器设置移到初始化阶段,只设置一次
状态: ✅ 已修复,待测试