在音频软件实现中经常会遇到两个模块采样率不一致的情况,比如语音通话时采集到的PCM信号是16k Hz的,但编码时codec是AMR-NB(AMR-NB是8k Hz采样),这时就需要把16k Hz采样的PCM值转换成8k Hz采样的PCM值(这叫降采样或者下采样),然后再去做AMR-NB编码。再比如音乐播放时音源是44.1k Hz采样率编码的,但是播放时是48k Hz采样率的,这就需要把解码后的44.1k Hz采样率的PCM值转换成48k Hz采样的PCM值(这叫升采样或者上采样),然后再送给codec芯片播放。采样率转换(sampling rate converter, SRC)或者叫重采样有多种方法,简单的有线性插值法(算法简单,load小,但性能一般),复杂的有sinc方法(算法复杂,load大,但性能较好)。好多的音频类开源代码里都有重采样的实现,比如PJSIP和FFMPEG。重采样的原理这里就不详细介绍了,有兴趣的可以看相关文章。今天我们讲的是如何去评估这些开源代码里的重采样实现以及选择最适合的实现。评估主要基于频响特性,其次看CPU load。评估的开源实现是我用过的PJSIP和SILK codec。PJSIP里的重采样有两种实现(线性插值和sinc),通过开关控制选用哪种。SILK里的重采样用的是三次插值方法。对其他开源实现的评估类似。
在评估前先要做一些准备工作,包括用CoolEdit生成10秒的扫频文件和包含重采样算法的应用程序。扫频文件作为应用程序的输入,输出就是重采样后的PCM文件,再看它的频谱特性。以下就是制作扫频文件的步骤:
1,在CoolEdit里新建一个指定采样率单声道的文件。如下图,采样率指定为16k Hz。
2,选中“生成”菜单里的“音调”,把“固定设置”√去掉,就变成了“初始设置”和“结束设置”。先初始设置,由于是16k Hz采样率,频率范围是50~7k Hz,在初始设置中设置基础频率为50Hz,同时设置持续时间为10秒,如下图:
再选择“结束设置”,设置基础频率为8k Hz,其他用默认设置,选择“确定”(如下图),一个扫频文件就做好了。
3,点击“频谱”按钮可以看到一个从50Hz到8000Hz 频谱都非常好的扫频文件,如下图:
扫频文件这样就做好了。再做重采样应用程序,各种系统下都可以做,我一般是在Ubuntu下做,比较简单,这里就不具体说了。扫频文件和重采样应用程序都做好后就开始评估了。我当时应用的场景是播放侧固定成48k采样,要把8k/16k/44.1k等升采样到48k。做了三个扫频文件,8k采样/16k采样/44.1k采样。先看SILK里的重采样和PJSIP里的线性插值方法在8k->48k和16k->48k下的表现,具体如下:
1,8k转48k: 初始是8k 窄带采样,范围是300~ 3400, 我做的扫频文件频率范围是200~4000. 原始扫频文件频谱如下:
1)pjsip 的线性插值方法,把扫频文件从8000上采样到48000,得到PCM文件频谱如下:
2)silk 的三次插值方法,把扫频文件从8000上采样到48000,得到PCM文件频谱如下:
从上面两图看出,上采样后频谱特性不好了。
2,16k转48k: 初始是16k 宽带采样,范围是50~7000, 我做的扫频文件频率范围是50~8000. 原始扫频文件频谱如下:
1)pjsip 的线性插值方法,把扫频文件从16000上采样到48000,得到PCM文件频谱如下:
2)silk 的三次插值方法,把扫频文件从16000上采样到48000,得到PCM文件频谱如下:
从上面两图看出,上采样后频谱特性也不好了。
再看PJSIP里的sinc方法在44.1k->48k下的表现。用sinc方法也有两种选择,分别是large_filter(滤波器个数达到8192个,load大)和small_filter(滤波器个数达到1536个,load相对小)。具体如下:44.1k转48k,范围是20~20000, 我做的扫频文件频率范围是20~20000. 原始扫频文件频谱如下:
1)large_filter下变成48000Hz的文件频谱,如下图:
2)small_filter下变成48000Hz的文件频谱,如下图:
从上面两图看出,用large_filter的since上采样后频谱特性依旧很好,用small_filter的since上采样后频谱特性在高频时有些不好。
从上面的频谱特性可以看出在播放侧做上采样到48k时最好用sinc的large_filter方法,它的频谱特性最好,但是load会非常高。sinc的small_filter方法频谱特性次之,只在高频时有点差,可接受。线性插值方法的频谱特性就不太好了。综上在这种情况下如果CPU load 不是问题就可用sinc的large_filter方法,如果CPU load 扛不住就用sinc的small_filter方法,线性插值方法由于频谱特性不好不建议用。我们当时是在400MHz的处理器上上做实现, load较为敏感,最终选择了sinc的small_filter方法。
至于其他的场景,方法类似,就不多说了。最后说一句,sinc方法是目前公认的最好的音频重采样方法,就是CPU load有点高,可以通过选择滤波器个数少或者优化的方法解决。