音频可视化,一言以蔽之,便是声音到图画的转化。
跟着视觉工业时代的到来,用户逐渐重视产品的极致体会,在市场上诸多优异的音乐类APP中,频谱动效 是一个经典的应用场景:
图片来源:咪咕音乐
本文以 Android
端为例,从音频信号 数据的获取、数据的处理、常见问题 几方面进行叙说,针对Android
端音频可视化的完结,供给一个通用可行的计划。
一、频谱数据的获取
1.时域与频域
制作频谱动效,首先要获取歌曲对应的旋律,这儿需求先对信号处理中 时域 和 频域 的概念有一个底子的认识:
时域(时刻域) 是描绘数学函数或物理信号对时刻的联系,例如一个信号的时域波形能够表达信号跟着时刻的改变;
频域(频率域) 是描绘信号在频率方面特性时用到的一种坐标系,纵轴是该频率信号的起伏,也便是一般说的频谱图。
关于咱们而言,时域虽然是实在存在,但信号的表达 晦涩难明;而频域比较前者则更加 简练直观。
因而,开发者需进行时域转频域的操作,这儿咱们借助 傅里叶改换, 将信号分解成振幅重量和频率重量,将函数的 时域(赤色) 与 频域(蓝色) 相关联:
借此咱们达到了从频域对模拟信号观察的意图,并将频谱展现给用户,然后提高用户听歌时沉浸式的体会感。
接下来,笔者针对时域转频域中运用到的 FFT算法(十分重要) 进行简略的介绍。
2.FFT-快速傅里叶改换
针对信号剖析的最底子办法,称为 离散傅里叶改换(Discrete Fourier Transform,下称DFT) 傅里叶剖析办法,它把信号从时域改换到频域,从而研究信号的频谱结构和改变规则。
在某些杂乱场景下,比方上图对应的有限长序列,运用 DFT
计算量会很大,很难实时地处理问题,因而咱们引出了 快速傅里叶改换 (fast Fourier transform, 下称FFT) 算法,其将 DFT
的运算量减少了几个数量级。
引进了这个概念,读者能够了解,为什么总是需求经过FFT
算法,拿到对应的数据输出才干完结制作,为了便于了解,咱们将对应输出的 byte[]
称为fft
,就和下面Android
官方的API
声明的相同:
public int getFft(byte[] fft) {
//...
}
3.原生API的优缺点
FFT
算法杂乱且易出错,Android
官方也供给了简略的 API
—— Visualizer
类便于开发者调用,只需求传入 audioSession
的 ID
,体系就能够主动捕获当时设备在播音频的信号,完结 fft
的转化后,再回调给开发者,开发者直接进行频谱的制作即可。
简略归纳其优势有:
- 1.体系主动完结音频信号起伏的采样,无需适配,适用于任何原生和三方播放器;
- 2.体系主动完结
fft
转化,开发者经过回调拿到数据直接制作,上手成本低。
因而,原生 Visualizer API
天经地义成为了完结计划的首选,但跟着完结的深化,更多问题不断暴露出来:
- 1.音量为
0
时,回调函数不会回来fft
数据; - 2.类本身仅仅一个
API
的壳,真实完结都是在native
层,难以针对性进行修正和扩展; - 3.在某些特殊机型有兼容性问题;
- 4.由于
Visualizer API
内部是从当时体系获取音频信号,因而运用前 有必要颁发麦克风权限,否则没有任何回来。
在实践开发开发中,上述缺点都是产品无法接受的,尤其是 3、4
两条,跟着近两年国家推行一系列用户隐私保护的政策,「展现UI特效需求麦克风权限」 是无法说服任何人的,综上所述,原生Visualizer API
计划被果断抛弃。
4.重整思路
现在又回到了原点,咱们需求自界说 Visualizer
。
详细的思路是,在数字化音频时,咱们会对信号起伏进行频繁的采样,这称为 脉冲编码调制 (下称PCM)。正如上文对 时域 的描绘,振幅随之被量化,但并不直观,因而,咱们需求针对 PCM
信号进行结构,即 FFT
算法。
——之前这一切都是交由原生Visualizer API
主动完结的,现在由咱们自己操控和完结,终究,咱们得到了每个时刻声音的频率,并将这些起伏在页面上展现给用户。
5.底层完结
怎么获取当时音频的 PCM
数据?这儿能够让底层播放器供给,社区比较完善的三方播放器(ijkplayer
、ExoPlayer
)等都供给了对应支撑,开发者能够根据本身事务调用API
或修正源码。
咱们不断获得一帧帧的 PCM
数据,一般这是一个 ByteBuffer
,这些 byte
或许来自多个 channel
,简化处理能够取一切 channel
的均匀值,这之后拿到这些数据进行傅里叶改换。
宏观上了解傅立叶改换,即 N
个时域点改换为 N
个频域点,瓜分采样频率 Fs
,频率间隔为△f=Fs / N
,根据奈奎斯特采样定理,采样频率是信号最高频率的2倍以上,故有用频点数为N / 2
,函数如下:
如上文所说,计算机直接进行傅立叶改换杂乱度为 O(N^2)
,一般是选用快速FFT
算法,杂乱度降低为 O(N*LogN)
,咱们无需手动完结,社区中已有老练安稳的 三方库 供给这样的支撑。
此外,傅立叶改换后的频域点为复数域,不方便做可视化,处理方式是忽略相位信息,仅取(实数)起伏信息。
6.功能优化
即便 FFT
算法比较较 DFT
有一定的优化,但实践履行的功能消耗仍然不能忽视,怎么针对这一点进行优化?
网易云音乐在 这篇文章 也有说到这个思路:首先,将FFT
算法履行放在播放进程的 native
层中,经过 JNI
调用回调给 java
层后,再将成果数据跨进程传输给主进程,再进行后续的制作操作:
除此之外,由于 PCM
每一帧都包括很多的数据,最初单次的成果 fft
是一个长度为 4096
的 float[]
——咱们可运用这些频率当即制作 4096
个条形图,但实践中底子没有这样的场景。
因而,在 FFT
算法履行前,咱们能够对不同频段进行初始数据的 二次采样。
根据声学,人耳能简略的分辨出100hz
和200hz
的腔调不同,但是很难分辨出8100hz
和8200hz
的腔调不同,尽管它们各自都是相差100hz
,能够说频率和腔调之间的改变并不是呈线性联系,而是某种对数的联系。
因而,咱们音频的采样以 低频段 为主, 高频段为辅,详细可参阅 维基百科。
这样,在 FFT
算法履行时,单次输入的数据源的巨细从初始的 4096
降低到了 64
或 128
,算法履行速度大幅提高。
二、数据的处理与制作
拿到 fft
数据后,接下来针对数据各个阶段遇到的问题,分步进行处理。
1.均匀算法
首先,由于数据是从不同频段的不同采样点进行获取,因而单个接连数据区间内会存在若干个数据忽然很高或很低的情况,这儿对数据进行简略的处理,进行 加权均匀:
2.拟合曲线
关于某一帧的数据而言,即便经过简略的加权均匀处理,对文章开头中间的柱状特效而言,展现作用仍然不佳,怎么将参差不齐的数据经过 平滑处理,构成视觉上的包络作用?
这儿咱们引进 曲线拟合,望文生义,便是用接连曲线近似地刻画或比较平面上离散点组所表示的坐标之间的函数联系的一种数据处理办法。
最小二乘法 是处理曲线拟合问题最常用的办法。其一般形式是:
引进之后,离散化的数据点构成一条润滑的曲线,能显着的提高用户的观感体会。
3.界说衰减系数
经过以上几步处理,单帧作用得到显着改进,但跟着划入时刻维度之后,相邻两帧的展现作用有显着的跳跃性,关于用户而言就好像掉帧相同。
这儿咱们引进 衰减系数,每一帧数据制作前,与上一帧数据进行对比,当单个频率点数据产生较高的颤动,经过衰减系数对颤动进行 按捺。
三、其它问题
1.声音和特效不同步问题
这个问题经过gif
图是无法表现的,但用户在听音乐时会当即注意到:频谱特效跳跃的太早了,总是比咱们实践听到的音乐节奏快了一步。
导致这个现象是由于自界说的播放器在数据传递给 Android
体系的 AudioTrack
之前,咱们已经在拿处理好的 fft
进行制作了,而播放器内部有自界说的缓冲区,这会导致视觉作用领先于音频作用,导致推迟输出。
处理这个问题也十分简略,只需针对PCM
数据,参阅缓冲区相关源码,再界说一份对应的ByteBuffer
即可。
2.制作计划
关于频谱制作的技能计划,能够挑选体系的 Canvas API
进行自界说View
,将数据的相关运算处理在CPU
中履行。
考虑到频谱动效本身偏 UI
的展现,且与用户没有直接、杂乱的交互,以及功能方面的思量,终究运用了 OpenGL
,并将数据运算相关处理经过矩阵改换,直接交给GPU
计算并烘托。
小结
本文针对 Android
端频谱特效完结的整体流程进行归纳性的描绘,由于频谱动画属于定制化较高的作用,读者无需纠缠于细节,而是将目光聚焦在实践问题的处理思路上。实践开发中,可根据本身产品的需求,灵活运用开发。
参阅资料
-
傅里叶改换-维基百科
-
快速傅里叶改换-百度百科
-
最小二乘法-百度百科
-
Android 音频可视化
-
译|Android Visualizer 可视化器的自界说完结