Android 音频可视化

Android音频可视化,指的是将音频的频率绘制到屏幕上,达到一种视觉效果,使播放或录制过程更加生动形象。

Android进行视频可视化涉及的三个主要知识点,其中比较难以理解的傅里叶变换公式。

  • Android原生的Visualizer使用(获取频率数据)
  • 傅里叶变换(音频从时域到频域变换理论)
  • 自定义View(展示频率数据)

一、开发难点

  • Android原生的Visualizer限制

    • 需要录音权限(播放音乐需要录音权限?)
    • 音量为0时,获取不到数据(有可能被误认为Bug)
    • 仅支持AudioTrack,MediaPlayer

    解决方案,自定Visualizer,可以参考末尾文章。

  • 傅里叶变换的理解

    如果从数学角度去推导和验证傅里叶变换,需要学习三角函数及其正交性、微积分、欧拉定理等等。感兴趣可看文章末尾B站视频。在这里,我们暂且知道傅里叶变换可以将函数分解成正余弦函数之和。在工程上应用,可以从时域变换到频域,从而可以观察一些特性。例如在音频上,在频率可以分析大多数男生为低频,女生为高频,可以进行变音处理和声纹模仿等应用。

    通过Visualizer可以拿到傅里叶变换后的数据,或者通过第三方库区计算。

  • 自定义View

    拿到频率数据,如何处理这些数据,并在View进行绘制。

二、Visualizer

比较庆幸的,Android原生为我们提供了Visualizer类,让我们可以快速得从音频获取原始的波形数据或快速傅里叶变换后数据。下面简单介绍其使用。

我们在创建AudioTrack或者AudioRecord实例后,可以获取对应的audioSessionId,用于创建Visualizer实例。

kotlin 复制代码
val visualizer = Visualizer(audioTrack.audioSessionId)

通过setCaptureSize函数设置采样率大小,其大小我们一般通过getCaptureSizeRange函数来获取。getCaptureSizeRange函数返回两个int类型数组,第一个表示最小值,第二个表示最大值,用来表示采样值的范围。

ini 复制代码
 visualizer.captureSize = Visualizer.getCaptureSizeRange()[1]

接着通过setDataCaptureListener获取采样数据回调。

arduino 复制代码
setDataCaptureListener(OnDataCaptureListener listener,int rate, boolean waveform, boolean fft)
  • OnDataCaptureListener 采样数据回调类,拥有onWaveFormDataCaptureonFftDataCapture两个函数,前者回调波形数据,后者回调傅里叶变换后数据。
  • rate 采样的频率,设置范围在0~Visualizer.getMaxCaptureRate()
  • waveform 是否返回波形数据,false的话,OnDataCaptureListeneronWaveFormDataCapture函数不会有回调。
  • fft 是否返回傅里叶变换后数据,false的话,OnDataCaptureListeneronFftDataCapture函数不会有回调。
kotlin 复制代码
visualizer.setDataCaptureListener(object : OnDataCaptureListener {
    override fun onWaveFormDataCapture(visualizer: Visualizer?, waveform: ByteArray?, samplingRate: Int) {

    }

    override fun onFftDataCapture(visualizer: Visualizer?, fft: ByteArray?, samplingRate: Int) {

    }
}, Visualizer.getMaxCaptureRate() / 2, false, true)

开始采样:

ini 复制代码
visualizer.enabled = true

退出界面或者停止,记得设置:

kotlin 复制代码
visualizer.enabled = false

三、自定义View

通过给Visualizer设置OnDataCaptureListener之后,可以onFftDataCapture函数中获取快速傅里叶变换后的数据,但如何处理返回后的fft数据呢?

通过FFT的数组格式,获取到每个频率点的实部和虚部。

scss 复制代码
val n = fft!!.size
val magnitudes = FloatArray(n / 2 + 1)
val phases = FloatArray(n / 2 + 1)
magnitudes[0] = Math.abs(fft[0].toInt()) as Float // DC

magnitudes[n / 2] = Math.abs(fft[1].toInt()) as Float // Nyquist

phases[0] = 0.also { phases[n / 2] = it.toFloat() }.toFloat()
for (k in 1 until n / 2) {
    val i = k * 2
    //取频率点实部与虚部的模
    magnitudes[k] = Math.hypot(fft!![i].toDouble(), fft!![i + 1].toDouble()).toFloat()
}

按照官方代码示例,我们去实数与虚数的模作为数据绘制点,模代表幅值的大小。

拿到数据magnitudes之后在View中进行绘制。

将每个点以条形状的形式画出:

ini 复制代码
mStrokeWidth = (mRect.width() - (mSpectrumCount - 1) * mItemMargin) / mSpectrumCount * 1.0f;
mPaint.setStrokeWidth(mStrokeWidth);
mPaint.setStyle(Paint.Style.FILL);
for (int i = 0; i < mSpectrumCount; i++) {
    canvas.drawLine(mRect.width() * i / mSpectrumCount, mRect.height() / 2, mRect.width() * i / mSpectrumCount, 2 + mRect.height() / 2 - mRawAudioBytes[i], mPaint);
}

代码地址

参考文章:

傅里叶分析之掐死教程(完整版)

B站视频 傅里叶变换公式推导

Visualizer

Android 音频可视化:频谱特效的探索与实践

Android Visualizer音频可视化------让你的音频跳动起来

相关推荐
雨白9 小时前
Jetpack系列(二):Lifecycle与LiveData结合,打造响应式UI
android·android jetpack
kk爱闹10 小时前
【挑战14天学完python和pytorch】- day01
android·pytorch·python
每次的天空12 小时前
Android-自定义View的实战学习总结
android·学习·kotlin·音视频
恋猫de小郭12 小时前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
断剑重铸之日13 小时前
Android自定义相机开发(类似OCR扫描相机)
android
随心最为安13 小时前
Android Library Maven 发布完整流程指南
android
岁月玲珑13 小时前
【使用Android Studio调试手机app时候手机老掉线问题】
android·ide·android studio
还鮟18 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡19 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi0019 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体