1 采样率转换
1.1 重新采样
Android 中的采样转化率也称为“重新采样”是每个系统常用的操作和功能。需求和数据源总是有不匹配的地方,比方说录音设置了16K采样率,而HAL层上来的数据默认是32K的,那就需要降采样。
采样率转换是将具有某一采样率的离散样本流更改为具有另一采样率的流的过程。采样率转换器(即重采样器)是执行采样率转换的模块。对于重采样器,原始流称为源信号,而重新采样的流称为设备信号。
在 Android 中有多处需要用到重采样器。例如,MP3 文件可能以 44.1kHz 的采样率编码,但需要在内部支持 48kHz 音频的 Android 设备上进行播放。在这种情况下,会使用重采样器将 MP3 输出音频从 44.1kHz 源采样率升采样到 Android 设备中使用的 48kHz 设备采样率。
重采样器的特性可以使用指标来表示,包括:
信号整体幅度的保存程度
信号频率带宽的保存程度(受设备采样率的限制)
通过重新采样器的整体延迟时间
有关频率的一致相位和群组延迟
计算复杂度(以 CPU 周期或功耗表示)
允许的源采样率和设备采样率的比率
动态更改采样率比率的能力
支持的数字音频采样格式
理想的重采样器具有如下特点:精确保留源信号的幅度和频率带宽(受接收器采样率的限制)、具有最短且一致的延迟时间、计算复杂度极低、允许任意动态转换比率,并且支持所有常见的数字音频采样格式。实际上,理想的重采样器并不存在,因为实际的重采样器会在这些特性上进行折衷。例如,理想质量的目标与短延迟时间和低复杂度相冲突。
Android 包括各种音频重采样器,因此可以根据应用使用情形和负载进行适当折衷。重采样器实现部分介绍了可用的重采样器。
1.2 重采样器实现
可用的重新采样器实现会频繁变化,并且可由原始设备制造商 (OEM) 自定义。默认重新采样器包括(按照信号失真降序和计算复杂度升序的顺序):
线性
立方
具有原始系数的 sinc
具有修订系数的 sinc
一般来说,sinc 重采样器更适合用来播放品质较高的音乐,而其他重采样器也应保留下来,以便在质量要求不那么高的情况(例如“按键声”或类似情况)下使用。
所选择的特定重采样器实现取决于使用情形、负载以及系统属性 af.resampler.quality
的值。如需了解详情,请参阅 AudioFlinger 中的音频重采样器源代码。
2 PCM音频单声道与双声道的相互转换
PCM音频数据每个样本在内存中的布局以下图为例,8个字节的位置,8位单声道可以存储8个样本,8位双声道能存储4个样本,16位单声道能存储4个,16位双声道只能存储2个。
2.1 单声道转双声道
单声道转双声道,我们需要把单声道的每一份数据都拷贝一份到右声道,这样就形成双声道的数据,就可以使用双声道播放了。
Java中可以用ArrayList来操作。录制的是16位的数据,所以我们每一个采样的数据会占据两位,所以在拷贝的过程中,我们也要每两位拷贝一次,下面举一个简单的例子:
private val convertMonoToStereoThread = Thread(Runnable {
// 单声道转双声道
// 双声道的存储格式为 LRLRLR
// 所以把左声道的内容拷贝到右声道即可
for (index in 0 until monoByteList.size step 2) {
// 目前保存的是16位的数据,所以要复制前两位
stereoByteList.add(monoByteList[index])
stereoByteList.add(monoByteList[index + 1])
// 目前保存的是16位的数据,所以要复制前两位
stereoByteList.add(monoByteList[index])
stereoByteList.add(monoByteList[index + 1])
}
convertCallback()
})
2.2 双声道转单声道
双声道转单声道有两种做法:
1、丢弃其中一路数据
可以丢失左声道或右声道的数据。我们可以按照单声道双声道的做法,每四位取前两位或后两位的数据即可。
如果转化为Short值处理的时候, 要注意大小端,录制的音频通常是低位在前,所以我们修改ByteBuffer默认的高位在前的配置:
ByteOrder.LITTLE_ENDIAN 从小到大 ,高位在后
ByteOrder.BIG_ENDIAN 从大到小,高位在前,默认
2、两路数据相加的平局值
如果保存的是16位双声道音频数据,可以转化为Short类型进行相加取平均值。