当前位置: 首页 > news >正文

随音舞动:Visualizer实现音频律动效果

🎬 前言

在许多音乐类 App 中,播放音频时的**律动柱状图(Spectrum)**或波形可视化是一个令人愉悦的视觉效果。
本文将介绍如何在 Android 中实现音频播放时的“跳动效果”,从最基础的 MediaPlayer + Visualizer 到不需要录音权限的 ExoPlayer + FFTAudioProcessor

通过本文你将学到:

  • 如何正确使用 Visualizer 实现波形/频谱捕获
  • 为什么需要录音权限
  • 如何使用 ExoPlayer 自定义 FFT 处理
  • 如何绘制一个平滑的柱状律动视图

⚙️ Visualizer 与权限问题

🎤 录音权限

Visualizer 是 Android 音频系统的一个特效类,用于实时获取播放流的波形或频谱数据。
如果未申请录音权限,Visualizer 无法初始化,会直接抛出异常:

java.lang.RuntimeException: Cannot initialize Visualizer engine, error: -3at android.media.audiofx.Visualizer.<init>(Visualizer.java:238)

解决方法:

<uses-permission android:name="android.permission.RECORD_AUDIO" />

🧩 初始化 Visualizer

推荐在 MediaPlayer.setOnPreparedListener 中初始化 Visualizer:

if (visualizer == null) {val audioSessionId = mediaPlayer.audioSessionIdvisualizer = Visualizer(audioSessionId).apply {captureSize = Visualizer.getCaptureSizeRange()[1]setDataCaptureListener(object : Visualizer.OnDataCaptureListener {override fun onWaveFormDataCapture(visualizer: Visualizer?, waveform: ByteArray?, samplingRate: Int) {// 可用于波形绘制}override fun onFftDataCapture(visualizer: Visualizer?, fft: ByteArray?, samplingRate: Int) {// FFT 频谱数据,可驱动可视化视图}}, Visualizer.getMaxCaptureRate() / 2, false, true)enabled = true}
}

🌈 绘制柱状律动 SpectrumView

以下是一个高性能、平滑的自定义频谱柱状图实现:

class SpectrumView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null
) : View(context, attrs) {var barWidth = 20f      // 每根柱子宽度var barSpace = 8f       // 柱子间距private var barCount = 0private lateinit var magnitudes: FloatArray       // 当前柱子高度private lateinit var targetMagnitudes: FloatArray // FFT 目标高度private val riseFactor = 0.4f   // 上升速度private val decayFactor = 0.05f // 下降速度private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {color = Color.GREENstyle = Paint.Style.FILL}// 计算柱子数量override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {super.onSizeChanged(w, h, oldw, oldh)barCount = ((w + barSpace) / (barWidth + barSpace)).toInt().coerceAtLeast(1)magnitudes = FloatArray(barCount)targetMagnitudes = FloatArray(barCount)}/** 更新 FFT 数据 */fun updateFFT(fft: ByteArray) {if (barCount == 0) returnval n = fft.size / 2val binPerBar = max(1, n / barCount)for (i in 0 until barCount) {var sum = 0ffor (j in 0 until binPerBar) {val index = (i * binPerBar + j) * 2if (index + 1 < fft.size) {val re = fft[index].toInt().toFloat()val im = fft[index + 1].toInt().toFloat()sum += sqrt(re * re + im * im)}}val avg = sum / binPerBartargetMagnitudes[i] = (avg / 128f).coerceAtMost(1f) // 归一化}invalidate()}override fun onDraw(canvas: Canvas) {super.onDraw(canvas)if (barCount == 0) returnval heightF = height.toFloat()for (i in 0 until barCount) {val target = targetMagnitudes[i]// 上升快,下降慢magnitudes[i] = if (target > magnitudes[i]) {magnitudes[i] + (target - magnitudes[i]) * riseFactor} else {magnitudes[i] * (1 - decayFactor)}val left = i * (barWidth + barSpace)val top = heightF - magnitudes[i] * heightFval right = left + barWidthval bottom = heightFcanvas.drawRoundRect(RectF(left, top, right, bottom), 8f, 8f, paint)}postInvalidateOnAnimation()}
}

✨ 特点:

  • 动态计算柱子数量,适应不同屏幕宽度
  • 上升快、下降慢,模拟物理惯性
  • 使用 postInvalidateOnAnimation() 提升动画流畅度

🧠 Visualizer 原理解析

  1. 工作机制

Visualizer 通过底层的 AudioFlinger(Android 音频混音服务)直接读取播放流的音频缓冲区。
它并不是“录音”,而是监听当前音频输出设备的数据,因此录音权限只是访问接口所需的安全授权。

整个过程如下:

MediaPlayer / AudioTrack↓
AudioFlinger(系统音频混音器)↓
Visualizer(数据捕获层)↓
应用层回调 OnDataCaptureListener

Visualizer 内部通过音频会话 ID(audioSessionId)将自身绑定到音频输出流,实现数据监听。

  1. 数据捕获类型
http://www.dtcms.com/a/528883.html

相关文章:

  • 重庆交通建设集团网站怎么做微信网站推广
  • 25-TensorFlow:概述Google开发的流行机器学习框架
  • 亚马逊云渠道商:AWS 本地 SSD 缓存是什么?
  • 苏州商城网站制作asp 免费网站模板
  • C. Serval 和公式
  • libevent库
  • c盘突然就满了怎么回事?怎么清理爆满的c盘?
  • 双流区规划局建设局网站网站开发分类列表
  • 10.4FormData :前端文件上传与表单数据处理的核心工具
  • 肇庆建网站在网站上显示备案信息
  • 如何批量获取蛋白质序列的所有结构域(domain)数据-1
  • 做兼职在线抠图网站本科自考科目有哪些
  • wordpress 下载模板站做的网站如何更换网站模板
  • Rust:Windows 系统 VsCode 环境搭建
  • 网站开发+接活创建一个网站的项目体现项目完成速度因素的
  • 双馈风力发电机控制系统仿真设计(论文+仿真)
  • 国内做性视频网站有哪些南京越城建设集团有限公司网站
  • Pytorch常用API(ML和DL)
  • 切水题2.0
  • 深入解析C++ String类的实现奥秘
  • 机器视觉的液晶电视OCA全贴合应用
  • 个人博客网站页面儿童玩具网站建设策划书
  • 构建大模型安全自动化测试框架:从手工POC到AI对抗AI的递归Fuzz实践
  • 数据库约束与查询:MySQL 中的 DQL 和约束全解析
  • C++笔记(面向对象)友元
  • 网站在工信部备案查询oa系统开发
  • FPGA基础知识(七):引脚约束深度解析--从物理连接到时序收敛的完整指南
  • Minecraft-Speed-Proxy——搭建专属的Minecraft加速IP
  • Flutter 异步 + 状态管理融合实践:Riverpod 与 Bloc 双方案解析
  • 10.25复习LRU缓存[特殊字符]