Android Visualizer

系统 Visualizer 提供了方便的 api 来获取播放音频的波形或 FFT 数据,一般使用方式是:

  1. 用 audio session ID 创建 Visualizer对象,传 0 可获取混音后的可视化数据,传特定播放器或 AudioTrack 所使用的 audio session 的

《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》

【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享

ID,可获取它们所播放音频的可视化数据
2. 调 setCaptureSize 方法设置每次获取的数据大小,调 setDataCaptureListener 方法设置数据回调并指定获取数据频率(即回调频率)和数据类型(波形或 FFT)
3. 调 setEnabled 方法开始获取数据,不再需要时调 release 方法释放资源

更详细的 api 信息可查看官方文档。

系统 Visualizer 输出的数据大小正比于音量,当音量为 0 时,输出也为 0,可视化效果会随音量变化。

使用系统 Visualizer 存在兼容性问题,在有些机型上会导致系统音效失效,如要在所有机型上都能无副作用地展示动效,需要实现自定义 Visualizer

自定义 Visualizer

作为跟系统 Visualizer 功能一致的数据源,自定义 Visualizer 需具备两个功能:

  • 获取 pcm 数据,计算 FFT
  • 以指定频率和大小发送 FFT 数据

实现第一个功能首先要获取播放音频的 pcm 数据,这要求使用的播放器能够提供 pcm 数据,我们的播放器是自己实现的,能够满足这个要求。我们对播放器进行了扩展,增加了收集解码后的 pcm 数据计算 FFT 的功能。

由于不同音频采样率不同,而计算 FFT 时采用固定的窗口大小,导致 FFT 计算结果回调频率随播放音频改变,同时指定的数据大小可能跟计算结果的大小不同,因此要实现第二个功能,需要对计算结果做固定频率和采样等处理。

另外,我们的播放器在播放进程中运行,而实际使用 FFT 数据的动效页面运行于主进程中,所以还需要跨进程传输数据。

综上,自定义 Visualizer 的整体流程是:在播放进程 native 层中计算 FFT,通过 JNI 调用,把计算结果回调给Java 层,然后通过 AIDL 把 FFT 数据传递给主进程进行后续的数据处理和发送操作。如下图所示:

固定频率需要将可变的 FFT 计算结果回调频率转换为外部设置的 Visualizer 回调频率,如下图所示:

根据所需数据发送时间间隔和 FFT 回调时间间隔差值的不同,我们采用两种不同的方式。

当时间间隔差值大于回调时间间隔时,每 t1/tt1/t 次回调发送一次数据,其中 t1 为所需数据发送时间间隔,t 为 FFT 回调时间间隔,如下图所示:

采样就是当外部设置的数据大小小于 FFT 计算结果的数据大小时,对原始 FFT 数据以合适的间隔抽取数据,以满足设置的要求。

为了让自定义 Visualizer 返回数据的取值范围跟系统 Visualizer 一致,从而实现数据源无缝切换,我们需要对 FFT 数据进行缩放。这里就需要用到前面提到的模与振幅的计算了,解码所得 pcm 数据的取值范围为 [-1, 1],所以原始信号振幅取值范围为 [0, 1],即 2M/N2M/N2M/N 的取值范围为 [0, 1](绘制时不会用到直流分量,这里不考虑);而系统 Visualizer 返回的 FFT 数据是一个 byte 数组,实部和虚部的取值范围为 [-128, 128],模的取值范围为 [0,128×2][0, 128 \times \sqrt2][0,128×2​],那么 2M/N×128×22M/N \times 128 \times \sqrt22M/N×128×2​ 的取值范围跟系统 Visualizer 输出 FFT 的模的取值范围一致。由于绘制不会用到相位信息,我们可以将用上述方式缩放后的值作为输出 FFT 数据的实部,并把虚部设为 0。

由于数据发送的频率较高,为了避免频繁创建对象导致内存抖动,我们采用对象池来保存数据数组对象,每次从对象池中获取所需大小的数组对象,填充采样数据后加入到队列中等待发送,数据消费完后将数组对象返回到对象池中。

数据处理

不同动效的具体数据处理方式不同,忽略细节上的差异,云音乐现有的动效中,除了宇宙尘埃和孤独星球,其他的处理流程基本一致,如下图所示:

首先根据动效选择的频率范围计算所需的频率数据在 FFT 数组中的索引位置:

然后根据动效所需数据点数,对频率范围内的 FFT 数据进行采样或用一个 FFT 数据表示多个数据点。

然后计算分贝:

其中 M 为 FFT 数据的模。

然后将分贝转化为高度:

其中 MAX_DB 是预设的分贝最大值,maxHeight 是当前动效要求的最大高度。

最后对计算出的高度做数据上的平滑处理。

平滑

对最终用来绘制的数据做平滑处理,可以得到更柔和的曲线,达到更好的视觉效果,如下图所示:

数据平滑算法有很多,我们综合考虑效果和计算复杂度选择了 Savitzky–Golay 滤波法,其计算方式如下,对应的窗口大小分别为5、7 和 9,可以按需选择不同的窗口大小。

经过平滑处理后数据的变化如下图所示:

BufferQueue

有些动效的数据处理计算比较复杂,为提升并行性,减少主线程耗时,我们借鉴系统图形框架中 BufferQueue 的思想,实现了一个简单的承载动效绘制数据,连接数据处理和绘制的 BufferQueue,其工作过程如下图所示:

在使用 BufferQueue 的动效绘制类初始化时,根据需要创建一个合适大小的 BufferQueue,并启动用于执行数据处理的 Looper 线程。

数据处理部分对应 BufferQueue 的 Producer,当 FFT 数据到来时,通过绑定 Looper 线程的 Handler 将数据发送到 Looper 线程中执行数据处理。数据处理时,首先调用 Producer 的 dequeue 方法从 BufferQueue 中获取空闲的 Buffer,然后对 FFT 数据进行处理,生成需要的数据向 Buffer 中填充,最后调用 Producer 的 queue 方法将 Buffer 加入到 BufferQueue 中的 queued 队列中。

绘制部分对应 BufferQueue 的 Consumer,调用 Producer 的 queue 方法时会触发 ConsumerListener 的 onBufferAvailable 回调,在回调中通过绑定主线程的 Handler 切换到主线程消费 Buffer。首先调用 Consumer 的 acquire 方法从 BufferQueue 的 queued 队列中获取 Buffer,然后从 Buffer 中取出所需数据来绘制,最后调用 Consumer 的 release 方法将上次的 Buffer 返回给 BufferQueue

euequeued队列中获取Buffer,然后从Buffer中取出所需数据来绘制,最后调用Consumerrelease方法将上次的Buffer返回给BufferQueue`。

Android高级架构师系统学习——Android-音频可视化,程序员怎样优雅度过35岁中年危机相关推荐

  1. apm性能监控系统,程序员怎样优雅度过35岁中年危机?大厂内部资料

    其实不是Android不行了,而是你跟不上了 我的很多读者都在反馈说,现在一个岗位可以收到的简历数,是前几年的几倍.我们必须承认,僧多粥少就是 Android 行业的现状,别说初中级工程师,就是高级工 ...

  2. 阿里Android高级架构师:一个牛逼的插件又双叒诞生了!

    作者:鸿洋 原文链接:https://mp.weixin.qq.com/s/GdDkzR8AvgHROUiGwYtfOg 前言 大家在写 Android 项目的时候,免不了要引入各种 Google 提 ...

  3. 十一年架构师教你怎么打造PHP程序员简历

    bilibili是国内知名的视频弹幕网站,这里有最及时的动漫新番,最棒的ACG氛围,最有创意的Up主.大家可以在这里找到许多欢乐. 哔哩哔哩是一个好网站,不仅可以找到欢乐,也能够给你提供学习途径.闲着 ...

  4. 【高级Java架构师系统学习】java十六进制字符串转数字

    一.字节跳动技术一面(算法) Java 的 16 进制与字符串的相互转换函数 JAVA 时间格式化处理 将毫秒转化为日期 文本的倒序输出 判断一个数字是奇数还是偶数 用Hibernate 实现分页 3 ...

  5. GitHub标星8-3k的学习习惯,未来的Android高级架构师:别让这几个点毁了你

    4.记得带上最少两份简历,你可以针对公司做不同的简历,没有任何一份简历是万能的,自己准备一支笔.现在虽然天气热,但别穿个短裤就跑来-至少也要打扮的像个上班族. 5.面试的时候一直在问你关于java,而 ...

  6. 大数据、云计算系统高级架构师课程学习路线图

    大数据之Linux+大数据开发篇 大数据的前景和意义也就不言而喻了,未来,大数据能够对大量.动态.能持续的数据,通过运用新系统.新 工具.新模型的挖掘,从而获得具有洞察力和新价值的东西.源于互联网的发 ...

  7. 2年从月薪8000程序员到Android高级架构师,我的逆袭之路。

    有时我们大谈架构,大谈设计模式,在实际的赶项目阶段都有些显得苍白无力. 一个项目团队,产品经理,技术经理,业务接口人,开发人员,架构师,这些人在一起共事,很难去协调好每个人心里想的.再加上开发成员的技 ...

  8. 架构 php_十年PHP架构师的成长之路,程序员必备

    不知不觉自己做开发已经十年了,这十年中我获得了技术能力.CTO.大公司的经历.但再仔细一想,这十年中我至少浪费了五年时间走了很多弯路,这五年可以足够让自己成长为一个优秀的程序员,我用这五年时间和很多程 ...

  9. IBM总架构师寇卫东:话说程序员的职业生涯-IT程序人生-职业生涯规划

    初级程序员和高级程序员时期,都属于职业生涯发展的第一阶段,我们可以称之为黄金时期.这阶段程序员的年龄在20~35岁之间,因为年轻,所以更善于学习,而且体力充沛,很多走过这个阶段的程序员有过通宵工作的经 ...

最新文章

  1. PyTorch数据加载处理
  2. 七 递归与二分法、匿名函数、内置函数
  3. P6222-「P6156 简单题」加强版【莫比乌斯反演】
  4. linux下分析cel文件,详细解析Linux scp命令的应用
  5. 劲爆ORACLE优化,你不必是专家
  6. pcb成型板aoi检测_smt贴片加工打样的检测设备
  7. 创建二叉树的代码_解二叉树的右视图的两种方式
  8. Json 读写操作中含有中文时
  9. Druid 在小米公司部分技术实践
  10. maven for Mac配置,idea 配置maven
  11. 板级电源究竟该怎么选型芯片?(实战篇)
  12. 节假日查询,一个简单很暴力的idea
  13. CrystalDiskInfo 各项参数说明
  14. Pr 入门教程如何创建动作序列?
  15. 劳动仲裁委员会的具体地址即(朝阳区酒仙桥南十里居28楼的具体路线)______转...
  16. ubuntu 使用LVM修改分区大小后开机报错的解决办法
  17. html重要知识点总结
  18. 【BP数据预测】斑点鬣狗算法优化BP神经网络数据预测【含Matlab 219期】
  19. (转载)解决FileOutputStream中文乱码问题
  20. 一文让你了解生成模型相关的解码Tricks

热门文章

  1. C/C++交通处罚单处理系统
  2. Python爬取bilibili的cos福利
  3. python装饰器–原来如此简单
  4. php connection reset,connection reset by peer问题总结及解决方案
  5. 支付宝怎么做风险控制?
  6. 开源规则引擎 drools
  7. Spring AOP 切入点表达式
  8. linux-mv命令使用
  9. Maya 凹凸贴图与置换贴图
  10. 马云的战略“三板斧”:使命、愿景、价值观