Android Visualizer FFT频段划分异常及-Infinity值问题排查
问题分析与修复方案
1. 采样率参数致命错误
你提到的samplingRate=44100000是明显笔误,常规音频采样率为44100(44.1kHz),多写的三个零直接导致所有频段索引计算完全失效:captureSize * a / samplingRate的结果趋近于0,最终生成1 downTo 0的无效索引范围。
2. 频段索引计算逻辑修正
Android Visualizer返回的FFT数据中,索引与频率的对应关系为:频率 = 索引k × 采样率 ÷ 捕获大小
反过来,目标频率对应的索引计算应为k = (目标频率 × 捕获大小) ÷ 采样率,同时需注意FFT的有效索引范围是0 到 captureSize/2(对应0Hz到采样率的一半,即奈奎斯特频率)。
修正后的频段索引计算代码:
val frequencyOrdinalRanges: List<IntRange> = targetEndpoints.zipWithNext { a, b -> val startK = if (a == 0f) 0 else (a * captureSize / samplingRate).toInt() // 限制最大索引不超过FFT有效范围 val endK = minOf((b * captureSize / samplingRate).toInt(), captureSize / 2) startK..endK }
- 0Hz对应的索引是0,无需额外加1
- 用正序
IntRange替代downTo,逻辑更直观 - 限制
endK避免访问超出FFT有效范围的索引
3. 对数计算的0值处理
当FFT数据的实部和虚部均为0时,Math.hypot(0,0)返回0,Math.log10(0)会得到-Infinity,直接导致结果无效。需添加极小偏移量避免该情况:
val hypot = Math.hypot(currentByte, nextByte) // 添加1e-6作为偏移量,避免对数计算出错 val logHypot = Math.log10(hypot + 1e-6)
4. 监听器计算逻辑优化
当前代码在循环内重复计算平均值属于冗余操作,应遍历完整个频段后再计算均值,同时增加索引越界判断:
override fun onFftDataCapture(visualizer: Visualizer, bytes: ByteArray, samplingRate: Int) { val output = DoubleArray(frequencyOrdinalRanges.size) for ((i, ordinalRange) in frequencyOrdinalRanges.withIndex()) { var logMagnitudeSum = 0.0 val count = ordinalRange.last - ordinalRange.first + 1 // 处理空范围(如目标频率超过奈奎斯特频率) if (count <= 0) { output[i] = 0.0 continue } for (k in ordinalRange) { val fftIndex = k * 2 // 避免数组越界 if (fftIndex + 1 >= bytes.size) break val currentByte = bytes[fftIndex].toDouble() val nextByte = bytes[fftIndex + 1].toDouble() val hypot = Math.hypot(currentByte, nextByte) val logHypot = Math.log10(hypot + 1e-6) logMagnitudeSum += logHypot } // 遍历完成后计算平均值 output[i] = logMagnitudeSum / count } // 后续处理output数组 }
5. 额外注意事项
- 采样率以
onFftDataCapture回调传入的samplingRate为准,不要自行硬写错误值 - Android 6.0+需动态申请
RECORD_AUDIO权限,Manifest中也要声明该权限 - 使用完
Visualizer后务必调用release()释放资源,避免内存泄漏
内容的提问来源于stack exchange,提问作者ILikeTacos
相关产品推荐
相关产品推荐

