|
在 Uniapp 中处理合并后的音频数据并实现安卓端播放,可以通过以下方案解决。**核心问题是:直接写入 PCM 数据无法播放,需要转换为 WAV 格式并使用合适的播放方式**:
### 完整解决方案(Uniapp + Android)
#### 1. 将合并的 PCM 转换为 WAV 格式
- // 全局参数(根据 COZE 实际配置调整)
- const SAMPLE_RATE = 16000;
- const BIT_DEPTH = 16;
- const CHANNELS = 1;
- // 转换函数
- function pcmToWav(pcmBuffer) {
- const header = new ArrayBuffer(44);
- const headerView = new DataView(header);
-
- // RIFF 头
- writeString(headerView, 0, 'RIFF');
- headerView.setUint32(4, 36 + pcmBuffer.byteLength, true);
- writeString(headerView, 8, 'WAVE');
-
- // fmt 块
- writeString(headerView, 12, 'fmt ');
- headerView.setUint32(16, 16, true);
- headerView.setUint16(20, 1, true); // PCM格式
- headerView.setUint16(22, CHANNELS, true);
- headerView.setUint32(24, SAMPLE_RATE, true);
- headerView.setUint32(28, SAMPLE_RATE * CHANNELS * (BIT_DEPTH / 8), true);
- headerView.setUint16(32, CHANNELS * (BIT_DEPTH / 8), true);
- headerView.setUint16(34, BIT_DEPTH, true);
-
- // data 块
- writeString(headerView, 36, 'data');
- headerView.setUint32(40, pcmBuffer.byteLength, true);
-
- // 合并 WAV 头 + PCM 数据
- const wavBuffer = new Uint8Array(44 + pcmBuffer.byteLength);
- wavBuffer.set(new Uint8Array(header), 0);
- wavBuffer.set(new Uint8Array(pcmBuffer), 44);
-
- return wavBuffer;
- }
- function writeString(view, offset, str) {
- for (let i = 0; i < str.length; i++) {
- view.setUint8(offset + i, str.charCodeAt(i));
- }
- }
复制代码
#### 2. 在安卓端写入并播放(使用 Uniapp API)
- // 转换 PCM 为 WAV
- const wavBuffer = pcmToWav(combinedBuffer);
- // 写入临时文件
- const filePath = `${plus.io.PUBLIC_DOCUMENTS}/audio_${Date.now()}.wav`;
- plus.io.requestFileSystem(plus.io.PRIVATE_DOC, (fs) => {
- fs.root.getFile(filePath, { create: true }, (fileEntry) => {
- fileEntry.createWriter((writer) => {
- writer.onwrite = (e) => {
- // 播放音频
- const audio = plus.audio.createPlayer(filePath);
- audio.play();
-
- // 播放结束自动删除(可选)
- audio.onEnded(() => {
- plus.io.resolveLocalFileSystemURL(filePath, (entry) => {
- entry.remove();
- });
- });
- };
- writer.write(new Blob([wavBuffer], { type: 'audio/wav' }));
- });
- });
- });
复制代码
#### 3. 流式播放方案(避免文件写入)
如果不想写入文件,可使用 Web Audio API 直接播放:
- function playAudioBuffer(buffer) {
- const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
-
- // 添加 WAV 头
- const wavBuffer = pcmToWav(buffer);
-
- audioCtx.decodeAudioData(wavBuffer.buffer, (audioBuffer) => {
- const source = audioCtx.createBufferSource();
- source.buffer = audioBuffer;
- source.connect(audioCtx.destination);
- source.start();
- });
- }
- // 使用示例
- playAudioBuffer(combinedBuffer);
复制代码
### 常见问题解决
#### 1. 写入文件卡住问题排查
- **权限问题**:确保 manifest.json 添加以下权限:
- "permission": {
- "Android": {
- "request": [
- "android.permission.WRITE_EXTERNAL_STORAGE"
- ]
- }
- }
复制代码
- **路径问题**:使用正确的前缀:
- // 推荐使用私有目录
- const filePath = `${plus.io.PRIVATE_WWW}/audio.wav`;
复制代码
#### 2. 音频参数不匹配问题
如果播放有杂音,检查 COZE 返回的实际参数:
- // 调试方法:查看前20字节判断格式
- console.log(
- '头部数据:',
- Array.from(new Uint8Array(combinedBuffer, 0, 20))
- );
复制代码
#### 3. 实时流式处理优化
对于增量数据,建议边接收边播放:
- const audioContext = new AudioContext();
- const audioQueue = [];
- // 处理每个片段
- function processChunk(chunk) {
- const wavChunk = pcmToWav(chunk);
- audioQueue.push(wavChunk);
- playNextChunk();
- }
- function playNextChunk() {
- if (audioQueue.length === 0) return;
-
- const chunk = audioQueue.shift();
- audioContext.decodeAudioData(chunk.buffer, (buffer) => {
- const source = audioContext.createBufferSource();
- source.buffer = buffer;
- source.connect(audioContext.destination);
- source.start();
- source.onended = playNextChunk;
- });
- }
复制代码
### 终极调试技巧
1. **导出音频文件检查**:
- // 将WAV文件保存到相册(用于电脑端分析)
- plus.gallery.save(filePath, () => {
- uni.showToast({ title: '音频已保存到相册' });
- });
复制代码
2. **在线验证工具**:
- 使用 https://audio-analyzer.com/ 上传导出的WAV文件
- 或使用Audacity导入原始PCM数据(需设置参数)
> 实测参数参考:COZE默认返回16000Hz采样率、16bit位深、单声道的PCM数据。如果仍有问题,建议检查字节序(WAV要求小端序)。
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。 |
|