• Android 音频可视化


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

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

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

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

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

    • 傅里叶变换的理解

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

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

    • 自定义View

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

    二、Visualizer

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

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

    val visualizer = Visualizer(audioTrack.audioSessionId)
    
    • 1

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

     visualizer.captureSize = Visualizer.getCaptureSizeRange()[1]
    
    • 1

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

    setDataCaptureListener(OnDataCaptureListener listener,int rate, boolean waveform, boolean fft)
    
    • 1
    • OnDataCaptureListener 采样数据回调类,拥有onWaveFormDataCaptureonFftDataCapture两个函数,前者回调波形数据,后者回调傅里叶变换后数据。
    • rate 采样的频率,设置范围在0~Visualizer.getMaxCaptureRate()
    • waveform 是否返回波形数据,false的话,OnDataCaptureListeneronWaveFormDataCapture函数不会有回调。
    • fft 是否返回傅里叶变换后数据,false的话,OnDataCaptureListeneronFftDataCapture函数不会有回调。
    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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    开始采样:

    visualizer.enabled = true
    
    • 1

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

    visualizer.enabled = false
    
    • 1
    三、自定义View

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

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

    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()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

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

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

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

    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Android 学习笔录

    Android 性能优化篇:https://qr18.cn/FVlo89
    Android Framework底层原理篇:https://qr18.cn/AQpN4J
    Android 车载篇:https://qr18.cn/F05ZCM
    Android 逆向安全学习笔记:https://qr18.cn/CQ5TcL
    Android 音视频篇:https://qr18.cn/Ei3VPD
    Jetpack全家桶篇(内含Compose):https://qr18.cn/A0gajp
    OkHttp 源码解析笔记:https://qr18.cn/Cw0pBD
    Kotlin 篇:https://qr18.cn/CdjtAF
    Gradle 篇:https://qr18.cn/DzrmMB
    Flutter 篇:https://qr18.cn/DIvKma
    Android 八大知识体:https://qr18.cn/CyxarU
    Android 核心笔记:https://qr21.cn/CaZQLo
    Android 往年面试题锦:https://qr18.cn/CKV8OZ
    2023年最新Android 面试题集:https://qr18.cn/CgxrRy
    Android 车载开发岗位面试习题:https://qr18.cn/FTlyCJ
    音视频面试题锦:https://qr18.cn/AcV6Ap

  • 相关阅读:
    小黑腰酸背痛腿抽筋的日常积累:多线程单例模式
    Strimzi Kafka Bridge(桥接)实战之一:简介和部署
    208道最常见的Java面试题整理(面试必备)
    如何在vuejs项目中使用md5加密密码
    Java.md
    Qt教程3-Ubuntu(x86_64)上配置arm64(aarch64)交叉编译环境及QT编译arm64架构工程
    使用 Pinia 轻松实现复杂的 Vue 3 状态管理
    科锐国际(计算机类),快手,CVTE,得物,蓝月亮,蓝禾,奇安信,顺丰,康冠科技,金证科技24春招内推
    MYSQL一站式学习,看完即学完
    第二章Java概述
  • 原文地址:https://blog.csdn.net/weixin_61845324/article/details/133828327