You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 17:27:25