本文作者:熊鋆洋 (网易云音乐大前端团队)
前言
音频可视化,顾名思义就是将声音以视觉的方式呈现出来。如何将音频信号绘制出来?如何将声音的变化在视觉上清晰的表现出来,让视觉和听觉上的感受一致?这些在 Android 上如何实现?本文将针对这些问题做出解答,尽量对 Android 上的音频可视化实现做一个全面的介绍。
傅里叶变换
Android 音频播放的一般流程是: 1. 播放器从本地音频文件或网络加载编码后的音频数据,解码为 pcm 数据写入 AudioTrack 2. AudioTrack 将 pcm 数据写入 FIFO 3. AudioFlinger 中的 MixerThread 通过 AudioMixer 读取 FIFO 中的数据进行混音后写入 HAL 输出设备进行播放
在这个流程中,直接体现音频特征,可用于可视化绘制的是 pcm 数据。但 pcm 表示各采样时间点上音频信号强度,看起来杂乱无章,难以体现听觉感知到的声音变化。pcm 数据仅可用来绘制体现音频信号平均强度变化的可视化动效,其他大部分动效需要使用对 pcm 数据做傅里叶变换后得到的体现各频率点上信号强度变化的频域数据来绘制。
这里简单回顾下傅里叶变换,它将信号从时域转换为频域,一般用于信号频谱分析,确定其成分。转换结果如下图所示:
pcm 数据是时间离散的,需要使用离散傅里叶变换(DFT),它将包含 N 个复数的序列 $\{x_n\}:=x_0, x_1, ..., x_{N-1}$ 转换为另一个复数序列 $\{X_k\}:=X_0, X_1, ..., X_{N-1}$,计算公式为:
X_k=\sum_{n=0}^{N-1}x_n \cdot e^{-i2 \pi {kn \over N}}=\sum_{n=0}^{N-1}x_n \cdot (cos(2\pi {kn \over N})-i \cdot sin(2\pi {kn \over N}))
直接用上面公式计算长度为 N 的序列的 DFT,时间复杂度为 $O(N^2)$,速度较慢,实际应用中,一般会使用快速傅里叶变换(FFT),将时间复杂度降为 $O(Nlog(N))$。
计算公式看起来很复杂,但不懂也不会影响我们实现音频可视化,FFT 的计算可以使用已有的库,不需要自己来实现。但为了从 FFT 的计算结果得到最终用来绘制的数据,有必要了解以下DFT特性: 输入全部为实数时,输出结果满足共轭对称性:$X_{N-k}=X_k^*$,因此一般实现只返回一半结果 如原始信号采样率为 $f_s$,序列长度为 N,输出频率分辨率为 $f_s/N$