需求小能手——播放pcm实时语音流

前言

前两周做了播放实时语音流的需求,就这一个需求跟后端联调了两天,最后还是拉外援才成功联调。这个需求本质也不是特别难,下面来看一下具体的实现过程

ws获取

标题中的语音流是实时的,公司项目是通过webSocket进行实时语音的获取,如果不是实时语音流,直接跳过。实时听起来很复杂,对于前端而言实现起来却很简单,调用ws几个方法就能实现。

  • 创建,new WebSocket实例化一个ws。
  • 连接,onopen监听ws连接状态。
  • 消息,ommessage监听ws连接的消息,send方法向ws发送消息。
  • 错误,onerror监听ws错误消息。
  • 关闭,onclose监听ws关闭,close方法关闭ws。

通过ws一系列操作我们就能实时获取后端传过来的数据。

语音流播放

如果直接传语音流前端直接当成流处理会很简单,但后端是通过调用其他平台获取的语音流,传到前端就变成字符串了,于是就是不断尝试的联调过程。

AudioContext

刚开始我的想法是通过AudioContext.createBufferSource()播放,该方法接收AudioBuffer对象,所以我们现在的目的就是将字符串转成AudioBuffer。获取AudioBuffer的方式有两种:

  • AudioContext.createBuffer():创建空白的AudioBuffer对象,然后将音频数据填充进去。
  • AudioContext.decodeAudioData():将ArrayBuffer(字节数组)数据解码为AduioBuffer。ArrayBuffer一般就是接口返回的文件流。
    对比两个方法选用第二个,第二个是需要传过来将字符串变为ArrayBuffer,跟后端沟通了下,反馈这字符串直接就是音频流,所以我就直接当成ArrayBuffer去处理。

传入字符串,运行报错,decodeAudioData解码不了这数据,看了不能直接用。搜索了一番,发现可以用charCodeAt将字符变为unicode编码,就尝试把字符串都变为uniCode再解析,还是不行,继续尝试。

base64编码

尝试了几次,跟后端商议把数据进行base64编码,前端拿到进行解码再进行处理。首先使用window.atob进行解码,然后用new ArrayBuffer创建为字节数组。

再次运行还是不行,因为是pcm流所以另一个同事引入了pcm-player依赖尝试进行播放。

pcm-player

pcm-player是一个简单的pcm流播放器,我们安装此依赖,再页面引入然后初始化一个实例。

然后我们将数据进行处理使用feed方法进行播放,注意该插件也是通过AudioContext.createBufferSource实现语音流播放,只是播放没有可视化的东西。实例化时我们要注意两个参数:

  • inputCodec:编码格式,表示类型化数组的类型。
  • sampleRate:采样率,录音相关属性。
    简单来说这两个属性影响着音质。引入了新插件,继续尝试,因为feed只接受类型化数据,所以我们要把解码后的数据变成TypedArray。我们先声明一个类型化数据,然后利用chatCodeAt将字节数据添加进去。注意,声明的TypedArray的类型要跟inputCodec保持一致。
js 复制代码
base64ToInt16Array(base64String) {
const rawData = window.atob(base64String)
const typedArray = new Int16Array(rawData.length)
for (let i = 0; i < rawData.length; i++) {
  typedArray[i] = rawData.charCodeAt(i)
}
return typedArray
}

因为是实时的,所以我们用watch监听ws的消息,将获取的字符串处理成TypedArray,然后用feed方法去播放。运行语音成功播放出来了,但是,但是音质不行。就因为音质问题跟后端对接了两天,改编码格式、采样率就是不能清晰播放出来,最终拉了一个外援,研究了一下午终于搞出来了。简单来说问题就是字节没对上,也就是前端处理方法有问题,上面给Int16Array赋值的时候需要把两个字节合成一个

到此录音能实时播放,并且播放的很清晰,完美收官。

总结

以上就是pcm清晰播放实时语音流的过程,只能说闻道有先后,术业有专攻 要不是大佬帮着分析字节问题,不知道还要卡多久。

相关推荐
陈随易26 分钟前
VSCode的Copilot扩展支持接入DeepSeek,Kimi了!
前端·后端·程序员
我不是外星人2 小时前
有了 Harness Engineering ,真的还需要研发工程师吗?
前端·后端·ai编程
IT_陈寒4 小时前
JavaScript的闭包把我坑惨了,说好的内存会自动回收呢?
前端·人工智能·后端
Jackson__5 小时前
分享一个横向滚动案例,带悬停暂停,通用性很强
前端
MariaH6 小时前
git rebase的使用
前端
_柳青杨6 小时前
深入理解 JavaScript 事件循环
前端·javascript
阡陌Jony6 小时前
关于前端性能优化的一些问题:
前端
用户600071819107 小时前
【翻译】简化 TSRX
前端
IT乐手8 小时前
佛德角逼平西班牙,国足还有啥借口?
前端
JustHappy8 小时前
我汇总了身边朋友的经历才发现,其实第一份实习是最难找的......
前端·后端·面试