【electron6】浏览器实时播放PCM数据

avatar
作者
筋斗云
阅读量:0

pcm介绍:PCM(Puls Code Modulation)全称脉码调制录音,PCM录音就是将声音的模拟信号表示成0,1标识的数字信号,未经任何编码和压缩处理,所以可以认为PCM是未经压缩的音频原始格式。PCM格式文件中不包含头部信息,播放器无法知道采样率,声道数,采样位数,音频数据大小等信息,导致无法播放。

需求:我需要在websoket中接收实时的pcm音频流原始数据:16位,16k,单通道(意思就是:pcm 的参数采样率16000,采样位数16,声道数1)

重要!:千万不要去运用什么所谓的插件,因为它会使你绕很大一圈的弯路,实时播放的pcm浏览器是支持的,使用插件很可能还会被迫读什么源码,也很容易将你带偏,再多的播放插件底层大部分也是基于AudioContext去开发的。

首先是拿音频文件试,但是却不了解mp3和wav的区别,就首选拿了mp3这是我的第一个深坑,因为mp3经过多层的处理压缩,已经距离原始的文件很远了,通过ajax拿回来的arraybuffer是可以直接塞进去CreateBufferSource.buffer中就能直接播放,原因就是decodeAdioData就直接能处理接口返回的arraybuffer数据

mp3示例:

useEffect(() => {       axios.request({         url: require('@/assets/wholeWorld.mp3'), // 假如这是我们从后端请求回来的音乐文件         responseType: 'arraybuffer', // 必须需要这个         method: 'get'       }).then(res => {         // 创建AudioBufferSourceNode         const arrayBuffer = res.data         const context = new AudioContext()         const source = context.createBufferSource();         context.decodeAudioData(arrayBuffer).then(audioBuffer => {           // 设置buffer属性         source.buffer = audioBuffer;         // 连接到音频上下文并播放         source.connect(context.destination);         })         source.start(0);       })   }, []) 

我处理wav的音频去播放,也是可以的使用decodeAudioData去进行播放

wav音频播放示例:

useEffect(() => {       axios.request({         url: require('@/assets/sample.wav'), // 假如这是我们从后端请求回来的音乐文件         responseType: 'arraybuffer', // 必须需要这个         method: 'get'       }).then(res => {         // 创建AudioBufferSourceNode         const arrayBuffer = res.data         const context = new AudioContext()         const source = context.createBufferSource();         context.decodeAudioData(arrayBuffer).then(audioBuffer => {           // 设置buffer属性         source.buffer = audioBuffer;         // 连接到音频上下文并播放         source.connect(context.destination);         })         source.start(0);       })   }, []) 

但是都不支持pcm文件,所以我最开始的思路是通过获取到的pcm去处理成wav的文件一样,去搜了很多资料,都说wav的文件比pcm只是多了44字节文件头,我没从深度验证,但是我通过加字节文件头去进行pcm的播放,因为decodeAudioData可以运行其他处理过的音频文件,尽管加了字节文件头是不能解决根本问题的,这段路我绕了很大一个圈。
mp3文件和wav文件的区别示例:
在这里插入图片描述
mp3和wav的区别说明:
在这里插入图片描述

其实浏览器是可以直接播放pcm数据的,无论是文件,还是socket返回来的原始数据,这过程涉及了Uint8转换Uint16,Uint16转成Float32,了解decodeAudioData和getChannelData究竟处理什么问题等知识。
首先,找一个pcm文件,进行播放调试,pcm的文件能播放成功,那socket就不是问题!

PCM播放代码示例:

(仅播放出声音的调试,自行调整代码规范)

// 解析PCM数据到AudioBuffer function decodePCM(arrayBuffer: ArrayBufferLike,sampleBits: number, channelCount: number, sampleRate: number,audioContext: any) {   return new Promise((resolve, reject) => {       const dataView = new DataView(arrayBuffer);       const length = (arrayBuffer.byteLength / (sampleBits / 8) / channelCount);       const buffer = audioContext.createBuffer(channelCount, length, sampleRate);       let offset = 0;        for (let channel = 0; channel < channelCount; channel++) {           const channelBuffer = buffer.getChannelData(channel);           for (let i = 0; i < length; i++) {               const sample = dataView.getInt16(offset, true); // 假设PCM数据是16位有符号整数               channelBuffer[i] = sample / 32768; // 标准化到-1到1的范围               offset += 2; // 16位 = 2字节           }       }        resolve(buffer);   }); } let sourceNode: any = null;   useEffect(() => {     // 加载音频文件     axios.request({       method: 'get',       url: require('@/assets/recorder.pcm'),       responseType: "arraybuffer",     }).then(res => {       const arraybuffer = res.data              const audioContext = new window.AudioContext();       decodePCM(arraybuffer, 16, 1, 16000, audioContext).then(buffer => {         sourceNode = audioContext.createBufferSource();         sourceNode.buffer = buffer;         sourceNode.connect(audioContext.destination);         sourceNode.start(); // 开始播放     }).catch(error => {         console.error('Error decoding PCM:', error);     });     })   }, []) 

注:
当声音有杂音,一直是一个杂音就说明,数据错了
当声音没有声音,数据很可能都是0
当声音隐隐有正常,但是杂音很重,一定是需要它:getChannelData和DataView
后端从TCP给我实时的pcm是压缩Uint8Array,前端将Uint8Array的数据解码为Uint16Array,然后合并Uint16Array的所有音频数据,还需要将Uint16Array通过DataView处理成AudioContext可播放的Float32Array,所以不了解AudioCotext API是很难解决这些问题的,更何况仅仅是播放的功能,我后面还要处理更多复杂的应用场景,持续更新实时播放pcm的处理应用场景。

最终在和蔼可亲的同事帮助和自己不辞辛苦的研究下搞出来了,感谢我的同事。

值得参考:
张鑫旭:https://www.zhangxinxu.com/wordpress/2023/10/js-audio-audiobuffer-concat-merge/
MDN:https://developer.mozilla.org/zh-CN/docs/Web/API/BaseAudioContext/createBuffer

推荐工具:
MEIDAINFO:https://mediaarea.net/MediaInfoOnline

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!