如何使用js-mp3解码MP3并在AudioContext中播放以解决Safari15解码问题
问题核心原因
你代码里有个非常隐蔽的低级错误:拼接右声道数据时,concatTypedArrays的第一个参数错误传成了已经累积了左声道采样的left数组,导致右声道数据混入了全部左声道的内容,最终左右声道数据错乱引发失真。
另外你当前的实现逻辑存在冗余:调用decoder.decode()之后已经拿到了完整的PCM数据,不需要再重置指针循环读帧,直接从返回的pcmArrayBuffer里拆分左右声道即可,效率更高也能避免逐帧拼接的性能问题。
修正后实现代码
// responseData 是MP3文件的ArrayBuffer const decoder = Mp3.newDecoder(responseData); // 直接获取完整的PCM数据 const pcmBuffer = decoder.decode(); const channels = decoder.numberOfChannels ?? 2; const sampleRate = decoder.sampleRate; // 按32位浮点数格式读取PCM const pcmData = new Float32Array(pcmBuffer); const frameCount = pcmData.length / channels; // 初始化AudioBuffer const audioContext = new AudioContext(); const audioBuffer = audioContext.createBuffer(channels, frameCount, sampleRate); // 拆分交错存储的PCM:js-mp3返回的PCM是左、右、左、右...交错排列的 for (let i = 0; i < frameCount; i++) { for (let ch = 0; ch < channels; ch++) { audioBuffer.getChannelData(ch)[i] = pcmData[i * channels + ch]; } } // 播放逻辑 const source = audioContext.createBufferSource(); source.buffer = audioBuffer; source.connect(audioContext.destination); source.start(0);
如果你非要保留原来的循环读帧写法,仅需要修改右声道拼接的一行代码即可:
把right = concatTypedArrays(left, decoder.frame.v_vec[1])修改为right = concatTypedArrays(right, decoder.frame.v_vec[1])就能解决失真问题,但该方案逐帧拼接的性能较差,处理长音频时容易出现卡顿,更推荐上方直接解析完整PCM的方案。
内容的提问来源于stack exchange,提问作者Einar Egilsson
相关产品推荐
相关产品推荐

