Ai_GirlFriend/xuniYou/NO_VALID_AUDIO_ERROR问题修复.md
2026-02-28 18:41:16 +08:00

6.0 KiB
Raw Blame History

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 是字符串!
    }
})

为什么会出错?

  1. encoding: 'binary' 返回的是字符串

    • uni-app 的 readFile 指定 encoding 后返回字符串
    • 不是 ArrayBuffer
  2. 字符串的 slice() 方法返回的还是字符串

    • audioData.slice(offset, end) 返回字符串片段
    • 不是二进制数据
  3. 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. 测试步骤

  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() 方法

// 发送文本
websocket.send({ data: "hello" })  // 文本消息

// 发送二进制
websocket.send({ data: arrayBuffer })  // 二进制消息

服务器端会根据数据类型自动判断:

  • 字符串 → msg["text"]
  • ArrayBuffer → msg["bytes"]

🎓 经验总结

关键教训

  1. 不要对二进制文件使用 encoding 参数

    • 音频、图片、视频等二进制文件
    • 不指定 encoding让它返回 ArrayBuffer
  2. 验证数据类型

    • 使用 instanceof ArrayBuffer 验证
    • 使用 byteLength 而不是 length
  3. 理解 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') {
            // 处理文本数据
        }
    }
})

🎉 预期结果

修复后,应该能够:

  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
状态: 已修复,待测试