前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android上实现频域均衡器

Android上实现频域均衡器

作者头像
天天P图攻城狮
发布2018-11-05 21:23:30
1.7K0
发布2018-11-05 21:23:30
举报
文章被收录于专栏:天天P图攻城狮天天P图攻城狮

作者简介:leilei, 天天P图AND工程师


本文主要分为三个部分:

1、现有的音控贴纸的创建以及渲染流程

2、从时域信息转化成频域信息的FFT算法实现

3、将生成的均衡器贴在3D眼镜的镜片上

一、现有的音控贴纸的创建以及渲染流程

现有的音控贴纸流程如图:

1)在配置文件里配置音控开关,音控幅度等参数

2)根据配置创建对应的滤镜加入滤镜链

3)帧渲染,实时计算音控因子调整贴纸大小

滤镜链的Filter创建较为复杂,不同的filter可以根据StickerItem里的多项参数决定自身是否需要创建:

应用上常用的Filter一般有BaseFilter,比如常用的CopyFilter;或者继承VideoBaseFilter,比如FaceCopyFilter等。音控素材继承自NormalVideoFilter,不但包含了TriggerCtrlItem可以根据多个参数控制参数更新:

而且包含了视频播放器和音频播放器(不再本次讨论范围了)。

NormalVideoFilter在每次要渲染之前都会更新参数,与音控相关的代码最终调用了TriggerCtrlItem的isAudioTriggered()函数,在这里获取分贝大小:

AudioDataManager是管理声音的单例类,默认提供麦克风录制的声音数据,或者接收并保存其他源的声音数据。

DecibelDetector类会异步处理麦克风声音数据频率是80ms/次,使用android系统的AudioRecord类实现。

二、从时域信息转化成频域信息的FFT算法实现

从第一节里可以看到原本音控的声音分贝数据db,来源于AudioDataManager类,默认麦克风的数据来源于DecibelDetector类,下面看看实现:

这里的BUFFER_SIZE是每次采样获得的时域数据长度。

采样频率32kHz、单声道、16位PCM编码方式得到的一个BUFFER_SIZE长度的short数组,即一次采样得到的声音时域数据。

原本的DB数据是这样来的:

简单来说就是对short数组里的每一项取绝对值、求和取平均、取对数再乘上系数。

基于现有的通路,获取FFT数据的函数:

下面详细介绍一下FFT的实现:

FFT是快速傅立叶算法的简称,要了解FFT,需要先介绍DFT,即离散傅立叶算法。

这里有一张DFT时域频域转化的图:

左边是时域的波形,右边是时域的数据。

网上有比较多的DFT或者FFT的介绍,这里我写一下个人理解,仅供参考:

1)DFT

DFT算法是时域转化频域的核心。FFT算法是其优化,所以先介绍DFT,再介绍FFT。

DFT的公式如下:

这里我简化一下输入和输出:

其中x(n)是输入的short数组,X(k)是DFT输出的频域数组,n的范围是[0 , N),k的范围是[0 , N).

这样DFT算法就把一个N长度的数组x,转化成了新的N长度的数组X。二者的区别是:

x数组的下标是固定的时间段,X数组的下标是固定的频率段,举个例子:x[0]代表0时刻的振幅,x[1]代表40ms时刻的振幅;X[0]代表频率为0的波的振幅总量,X[1]代表频率为20Hz的波的振幅总量。

```

注意:

1)这里的数组的下标意义源于这段声音本身的属性:采样率,声道等.如果采样频率大,在N不变的情况下数组下标的频率间隔越大。

2)不同的FFT算法只能影响频率数据的精度,不能改变其最大的频域范围。

3)由于存在频率混叠,FFT的得到的最大频率是采样率的一半。FFT的数据只有前N/2的数据有效,后一半的数据与前一半数据完全对称。

```

明确了输入和输出结果后,再来看一下展开的DFT的公式:

k = [0, N)。这里X(0)的计算需要从x[0]到x[N-1]的数据,每计算一个X数据,都要遍历一遍输入数据,时间复杂度是O(N^2)。DFT公式的原理和行列式表示比较复杂,留在下篇文章再讲。

2)FFT

FFT算法可以将DFT优化到O(NlogN)的复杂度,这里介绍一下基2点FFT算法。优化DFT算法的经典思路是分治,基2点FFT算法就是2分的DFT算法的一种:

将一个长度为N的输入子集划分成2个N/2的子集分别计算,直到划分长度为2的N/2个子集,最后计算2点DFT即可。

这里不详细展开公式的计算,简单说一下使用时的关键点:划分子集。FFT的划分不是简单折半划分,需要奇偶划分:

X(k)是下标为[0 - N-1]的数据集,划分成G(k)和H(k);

G(k)的下标为0, 2, 4, ……, N-4, N-2,而H(k)的下标为1, 3, 5, …… ,N-3, N-1。

这里k的范围变了:k = [0, N/2)。

DFT的计算因子,每一轮计算的都只需要计算k次因子。轮次为logN。

1)X(k)的周期为N

2)G(k),H(k)周期为N/2, k的下标均为[0 , N/2)

3)

将上面的公式用蝶形图表示:

这里将N周期的X集合,分解成了N/2周期的G集合和H集合。若N=2,直接得到结果。下面看看N=8的蝶形图:

可以看到左边x(k)的顺序变了,因为奇偶划分的原因。为了方便计算,在自底向上计算FFT之前需要倒序,倒序算法如下:

蝶形图左边的x(k)的下标即为排序算法结果。xin[k]即为排序好的复数数组x(k)。蝶形计算图如下:

cc为复数乘法,cut为复数减法,sum为复数加法。每一轮计算的中间结果都保存在xin对应位置。最终得到了FFT的X(k)结果。更多的实现细节请参考:https://www.cnblogs.com/Free-Thinker/p/4759949.html

三、将生成的均衡器贴在3D眼镜的镜片上

这部分是基于现有的3D贴纸素材实现的。要将均衡器贴在3D镜片上,需要获取当前3D眼镜的镜片材质,再将均衡器贴在上面。3D贴纸的实现使用了gameplay引擎。这里不详细介绍gameplay了(主要是我也不太懂),简单说明一下:

3D贴纸的素材是以node作为单位,在visitScene(Node *node)函数里会解析每一个node(包括镜片里的素材图)。在配置参数重添加“__audio__”的tag来标示使用,在解析的时候保存好该texture即可。

这样就把上部分渲染好的texture关联到了3D上,最后效果图如下:

四、总结

本篇文章主要介绍了将录音从时域数据转化成频域数据的方法,所有代码和具体实现都是基于Android的,其中FFT的代码源于互联网,FFT的讲解部分多半源于K.R.Rao的《快速傅里叶变换:算法与应用》。FFT算法博大精深,本文主要介绍了基2点的FFT的实现,还有基3点,基4点甚至有2维、3维等等FFT算法,如果读者有兴趣,可以参见该书。对于将均衡器贴在3D模型上,其实涉及到了OpenGL的复杂应用,不过得益于现有应用上的优秀的代码封装,哪怕像我这样的新手也能稍作变化,实现比较炫酷的3D音控效果。

参考文献:

【1】https://www.cnblogs.com/luoqingyu/p/5930181.html

【2】 K.R.Rao 等著,万帅等译《快速傅里叶变换:算法与应用》

【3】https://www.cnblogs.com/Free-Thinker/p/4759949.html

文章后记: 天天P图是由腾讯公司开发的业内领先的图像处理,相机美拍的APP。欢迎扫码或搜索关注我们的微信公众号:“天天P图攻城狮”,那上面将陆续公开分享我们的技术实践,期待一起交流学习!

加入我们: 天天P图技术团队长期招聘: (1) AND / iOS 开发工程师 (2) 图像处理算法工程师  期待对我们感兴趣或者有推荐的技术牛人加入我们(base 上海)!联系方式:ttpic_dev@qq.com

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-11-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 天天P图攻城狮 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
流计算 Oceanus
流计算 Oceanus 是大数据产品生态体系的实时化分析利器,是基于 Apache Flink 构建的企业级实时大数据分析平台,具备一站开发、无缝连接、亚秒延时、低廉成本、安全稳定等特点。流计算 Oceanus 以实现企业数据价值最大化为目标,加速企业实时化数字化的建设进程。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档