Android上实现频域均衡器

作者简介: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

原文发布于微信公众号 - 天天P图攻城狮(ttpic_dev)

原文发表时间:2018-11-05

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏hadoop学习笔记

Hanlp自然语言处理工具的使用演练

Hanlp是由一系列模型与算法组成的工具包,目标是普及自然语言处理在生产环境中的应用。Hanlp具备功能完善、性能高效、架构清洗、语料时新、可自定义的特点;提供...

1992
来自专栏Java面试通关手册

六道面试中常见的智力题 来看看你会做几道?

下面的题目来自滴滴出行2017秋招题。这些题目是我自己觉得比较难或者比较容易出错的题目。

2264
来自专栏奇点大数据

遗传算法(2)

在遗传算法中我们再举一个求极大值的例子。这种例子也是比较多见的,只要我们把一些数据关系描述成函数之后就会有一些求极大值或者极小值的问题。 其实极大值和极小值是一...

34612
来自专栏余林丰

初学数据挖掘——相似性度量(二)

  上一篇中介绍了四个算法,并用四个算法分别计算了两个人的相似度。这篇就来讲讲相似性算法在实际当中怎么用。第一:将指定的人与其他人作相似性比较,并从高到低进行排...

2386
来自专栏开发 & 算法杂谈

动态数据竞争检测方法实验分析(一)

之前的文章大致介绍了一下我们的动态数据竞争检测平台如何构建,这篇文章主要是在动态数据竞争检测平台上实现了之前介绍的数据竞争检测方法,我们扩展了其中的一些方法使得...

2242
来自专栏数据结构与算法

洛谷P4012 深海机器人问题(费用流)

题目描述 深海资源考察探险队的潜艇将到达深海的海底进行科学考察。 潜艇内有多个深海机器人。潜艇到达深海海底后,深海机器人将离开潜艇向预定目标移动。 深海机器人在...

3547
来自专栏C语言及其他语言

[每日一题]老王赛马

题目描述 赛马是一古老的游戏,早在公元前四世纪的中国,处在诸侯割据的状态,历史上称为“战国时期”。在魏国作官的孙膑,因为受到同僚庞涓的迫害,被齐国使臣救出后,到...

2749
来自专栏趣学算法

ACM竞赛学习指南(算法工程师成长计划)

6791
来自专栏WOLFRAM

用 Wolfram 语言制作圣诞动画

2132
来自专栏大数据文摘

改变计算技术的 9 个伟大算法

2423

扫码关注云+社区

领取腾讯云代金券