在QQ新版的8.0里有这样一个设计:语音进度条功能,上线之后突然登上微博热搜,那么这个温馨的功能,是如何开发出来的呢?它的技术难点又在哪里?我们有第一手资料,为你还原~
现有的这些点击播放的语音气泡真的满足了所有用户需求吗?emm…
总结一下:发送语音一时爽,接收语音想撞墙。。。
1、语音消息支持点击暂停、继续播放,再也不用崩溃地“从头再来”。
2、语音消息支持进度拖动,想听哪里听哪里。
3、语音消息支持展示音量频谱,声音大小一看便知。
整个需求可以简化成三个子过程:
1、 音频数据转波形
2、 接收方音频数据的来源
3、 播放器支持暂停、恢复和拖动操作。这里主要介绍过程一和过程二。
这里主要涉及到波纹真实性问题和波纹美观两个问题。
波纹真实性是指如何用较少的波纹个数代表较多的原始PCM数据,同时保证波纹的高振幅部分代表声音响亮的部分。
波纹美观主要问题包括:1、波形持续高的地方,波形较平;2、声音突然停止,音柱落差较大
声纹真实性:
问题一、人声振幅局限
人声振幅局限:从系统拿到的原始PCM数据声音区间 IOS [0,1],android [0-32767],但人说话最大只能达到ios[0-0.32],android[0-10297],大概33%左右,导致录制的声音即使很大,波形也振幅也不高。
解决办法:数据归一化,将采集数据归一化到区间[0,255], IOS 使用函数min((x10/0.32)255,255),android使用函数min((x/(32767/255))*255/80,255)。
问题二 、波形采样
声纹的最大个数为28,但采样率16K,会员最长录音5分钟,录制数据可达到5*60*16K,怎么通过使用28个波形来代表5*60*16K个原始数据。
解决办法:一个波形代表一定时间的数据,整个原始波形被划分成一个一个的区间。
如何代表:一种是取最大值,一种是取均值,并进行试验对比。
均值的取值方式:
综合以上实验,抽样方式采用取区间方式,在区间内取均值,且以四舍五入取区间的方式进行抽样。
声纹美化:
在产品灰度和内部用户体验过程中,反馈的一个主要问题是声纹波形不美观。经过和设计产品讨论,得到不美观的两个原因及解决方案:
1.声音说话一直很大时,音柱高度达到最大导致波形较平。
[ 波形较平 ]解决方案:因为是在绘制波形时添加的随机处理,为了保证UI刷新时波形不变,这里使用一种伪随机算法:以声纹个数作为随机种子,取余计算波形周期。判断音柱的高度,当超过一定值时,对音柱做正弦波处理,平滑语音波形。效果对比:
优化前2.声音突然从低到高或者从高到低时,音柱高度落差过大,导致波形参差不齐。
[ 音柱落差较大 ]解决方案:减速插值和平均综合
减速插值器:低振幅波纹绘制高度递增大于高振幅波纹绘制高度递增,下图形象说明:
减速插值器使用的函数为y=1-(1-t)^(2f) ,通过f(factor)去调整减速的大小,在实际过程中,经过对比,f取值为1.5,即对应上面绿色的线。
平均综合:在减速插值后,如果相邻两根音柱高度差仍较大,超过阈值时,则拔高低的音柱,从而降低音柱的落差。拔高的方式主要使用平均值法,即取两条音柱高度的平均。效果对比:
优化前经过上述两种波形处理后,在保持用户能够区分语音有无声音的同时,也有一个相对美观的视觉效果。
接受方有两种方式产生波形数据,一种是接受数据后在本地解码,产生原始PCM数据(接收方解码方案);第二种在发送方,发送方将处理好的波形数据点发送给接受方(发送方传输波纹特征方案)。如何在这两种方案间取舍?先各自分析一下每个方案的优缺点:
接收方解码方案
优点:
1、 Ptt消息传输过程完全不需要改动
2、 兼容各个版本
缺点:
性能较差,每收到一个ptt都需要额外开启一个解码线程去解码,在群聊天中,如果有大量的ptt消息,会有很大的性能问题。
发送方传输波纹特征方案
缺点:
1、 Ptt消息传输需要增加波纹特征
2、 PC版本和老版本没有波纹特征数据,无法绘制波纹
优点:
接受方不需要解码没有性能问题。
结论:虽然接收方解码方案比发送方传输波纹特征方案的有点还多一个,但是接收方解码方案的性能问题是致命的,没有好的方法去优化同一时间接收大量ptt消息场景。因此选择了发送方传输波纹特征方案,同时为了兼容各个版本,在没有波纹特征数据时,接收方会随机生成波纹来统一交互。
语音进度条发布后,很好的满足了用户在收听过程中被打断时需要暂停和继续,以及需要重复收听时可以灵活地选择起始位置的诉求。大大地节省了时间用户收听语音消息的时间,提高了效率。接下来,我们还将继续挖掘用户在语音沟通中的痛点和诉求,新增语音消息倍速播放、语音自动转文字等功能,进一步提供用户的沟通效率,让沟通更加灵活和便捷。
本文系作者在时代Java发表,未经许可,不得转载。
如有侵权,请联系nowjava@qq.com删除。