6.0 KiB
6.0 KiB
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 - 音频数据无效
🔍 根本原因
问题代码
fs.readFile({
filePath: res.tempFilePath,
encoding: 'binary', // ❌ 错误!这会返回字符串,不是 ArrayBuffer
success: (fileRes) => {
this.sendAudioInChunks(fileRes.data) // fileRes.data 是字符串!
}
})
为什么会出错?
-
encoding: 'binary'返回的是字符串- uni-app 的
readFile指定 encoding 后返回字符串 - 不是 ArrayBuffer
- uni-app 的
-
字符串的
slice()方法返回的还是字符串audioData.slice(offset, end)返回字符串片段- 不是二进制数据
-
WebSocket 发送字符串时会被当作文本消息
- 服务器收到的不是二进制音频数据
- 而是文本字符串
- ASR 无法识别,报错
NO_VALID_AUDIO_ERROR
✅ 修复方案
修复后的代码
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 也增加了验证
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')
typeof fileRes.data // "string"
fileRes.data instanceof ArrayBuffer // false
fileRes.data.length // 字符串长度(可能不等于字节数)
fileRes.data.slice(0, 10) // 返回字符串片段
正确的方式(不指定 encoding)
typeof fileRes.data // "object"
fileRes.data instanceof ArrayBuffer // true
fileRes.data.byteLength // 字节数
fileRes.data.slice(0, 10) // 返回 ArrayBuffer 片段
🔧 如何测试
1. 重新编译客户端
在 HBuilderX 中重新运行项目到手机/模拟器
2. 测试步骤
- 打开 App,进入语音通话页面
- 按住"按住说话"按钮
- 说话 3-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() 方法
// 发送文本
websocket.send({ data: "hello" }) // 文本消息
// 发送二进制
websocket.send({ data: arrayBuffer }) // 二进制消息
服务器端会根据数据类型自动判断:
- 字符串 →
msg["text"] - ArrayBuffer →
msg["bytes"]
🎓 经验总结
关键教训
-
不要对二进制文件使用 encoding 参数
- 音频、图片、视频等二进制文件
- 不指定 encoding,让它返回 ArrayBuffer
-
验证数据类型
- 使用
instanceof ArrayBuffer验证 - 使用
byteLength而不是length
- 使用
-
理解 WebSocket 的数据类型
- 字符串和二进制数据的处理方式不同
- 服务器端会根据类型分别处理
最佳实践
// ✅ 读取二进制文件的正确方式
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') {
// 处理文本数据
}
}
})
🎉 预期结果
修复后,应该能够:
- ✅ 正确读取 PCM 音频文件为 ArrayBuffer
- ✅ 正确切片 ArrayBuffer
- ✅ 正确发送二进制数据到服务器
- ✅ 服务器 ASR 正确识别音频
- ✅ 不再出现
NO_VALID_AUDIO_ERROR错误 - ✅ 完整的对话流程:ASR → LLM → TTS
📞 如果还有问题
如果修复后还是出现 NO_VALID_AUDIO_ERROR,可能的原因:
-
音频格式不对
- 确认录音格式为 PCM
- 确认采样率为 16000Hz
- 确认单声道
-
音频太短
- 至少录音 3 秒
- 查看日志中的 "预计录音时长"
-
音频质量差
- 在安静环境测试
- 清晰发音
- 避免背景噪音
修复时间: 2026-02-28
问题: NO_VALID_AUDIO_ERROR
原因: 使用 encoding: 'binary' 导致发送字符串而不是二进制数据
解决: 不指定 encoding,让 readFile 返回 ArrayBuffer
状态: ✅ 已修复,待测试